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
283 static struct LevelFileConfigInfo chunk_config_ELEM[] =
285 // (these values are the same for each player)
288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
289 &li.block_last_field, FALSE // default case for EM levels
293 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
294 &li.sp_block_last_field, TRUE // default case for SP levels
298 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
299 &li.instant_relocation, FALSE
303 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
304 &li.can_pass_to_walkable, FALSE
308 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
309 &li.block_snap_field, TRUE
313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
314 &li.continuous_snapping, TRUE
318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
319 &li.shifted_relocation, FALSE
323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
324 &li.lazy_relocation, FALSE
328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
329 &li.finish_dig_collect, TRUE
333 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
334 &li.keep_walkable_ce, FALSE
337 // (these values are different for each player)
340 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
341 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
346 &li.initial_player_gravity[0], FALSE
350 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
351 &li.use_start_element[0], FALSE
355 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
356 &li.start_element[0], EL_PLAYER_1
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
361 &li.use_artwork_element[0], FALSE
365 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
366 &li.artwork_element[0], EL_PLAYER_1
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
371 &li.use_explosion_element[0], FALSE
375 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
376 &li.explosion_element[0], EL_PLAYER_1
380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
381 &li.use_initial_inventory[0], FALSE
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
386 &li.initial_inventory_size[0], 1
390 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
391 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
392 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
397 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
398 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
403 &li.initial_player_gravity[1], FALSE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
408 &li.use_start_element[1], FALSE
412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
413 &li.start_element[1], EL_PLAYER_2
417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
418 &li.use_artwork_element[1], FALSE
422 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
423 &li.artwork_element[1], EL_PLAYER_2
427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
428 &li.use_explosion_element[1], FALSE
432 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
433 &li.explosion_element[1], EL_PLAYER_2
437 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
438 &li.use_initial_inventory[1], FALSE
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
443 &li.initial_inventory_size[1], 1
447 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
448 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
449 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
454 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
455 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
460 &li.initial_player_gravity[2], FALSE
464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
465 &li.use_start_element[2], FALSE
469 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
470 &li.start_element[2], EL_PLAYER_3
474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
475 &li.use_artwork_element[2], FALSE
479 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
480 &li.artwork_element[2], EL_PLAYER_3
484 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
485 &li.use_explosion_element[2], FALSE
489 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
490 &li.explosion_element[2], EL_PLAYER_3
494 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
495 &li.use_initial_inventory[2], FALSE
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
500 &li.initial_inventory_size[2], 1
504 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
505 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
506 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
511 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
512 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
517 &li.initial_player_gravity[3], FALSE
521 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
522 &li.use_start_element[3], FALSE
526 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
527 &li.start_element[3], EL_PLAYER_4
531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
532 &li.use_artwork_element[3], FALSE
536 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
537 &li.artwork_element[3], EL_PLAYER_4
541 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
542 &li.use_explosion_element[3], FALSE
546 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
547 &li.explosion_element[3], EL_PLAYER_4
551 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
552 &li.use_initial_inventory[3], FALSE
556 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
557 &li.initial_inventory_size[3], 1
561 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
562 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
563 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
568 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
569 &li.score[SC_EMERALD], 10
574 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
575 &li.score[SC_DIAMOND], 10
580 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
581 &li.score[SC_BUG], 10
586 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
587 &li.score[SC_SPACESHIP], 10
592 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
593 &li.score[SC_PACMAN], 10
598 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
599 &li.score[SC_NUT], 10
604 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
605 &li.score[SC_DYNAMITE], 10
610 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
611 &li.score[SC_KEY], 10
616 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
617 &li.score[SC_PEARL], 10
622 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
623 &li.score[SC_CRYSTAL], 10
628 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
629 &li.amoeba_content, EL_DIAMOND
633 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
638 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
639 &li.grow_into_diggable, TRUE
644 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
645 &li.yamyam_content, EL_ROCK, NULL,
646 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
650 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
651 &li.score[SC_YAMYAM], 10
656 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
657 &li.score[SC_ROBOT], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.time_magic_wall, 10
679 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
680 &li.game_of_life[0], 2
684 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
685 &li.game_of_life[1], 3
689 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
690 &li.game_of_life[2], 3
694 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
695 &li.game_of_life[3], 3
699 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
700 &li.use_life_bugs, FALSE
705 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
710 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
715 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
720 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
725 EL_TIMEGATE_SWITCH, -1,
726 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 &li.time_timegate, 10
731 EL_LIGHT_SWITCH_ACTIVE, -1,
732 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
737 EL_SHIELD_NORMAL, -1,
738 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 &li.shield_normal_time, 10
742 EL_SHIELD_NORMAL, -1,
743 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
744 &li.score[SC_SHIELD], 10
748 EL_SHIELD_DEADLY, -1,
749 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
750 &li.shield_deadly_time, 10
753 EL_SHIELD_DEADLY, -1,
754 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
755 &li.score[SC_SHIELD], 10
760 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
765 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
766 &li.extra_time_score, 10
770 EL_TIME_ORB_FULL, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 &li.time_orb_time, 10
775 EL_TIME_ORB_FULL, -1,
776 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
777 &li.use_time_orb_bug, FALSE
782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
783 &li.use_spring_bug, FALSE
788 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
789 &li.android_move_time, 10
793 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
794 &li.android_clone_time, 10
797 EL_EMC_ANDROID, SAVE_CONF_NEVER,
798 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
799 &li.android_clone_element[0], EL_EMPTY, NULL,
800 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
804 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
805 &li.android_clone_element[0], EL_EMPTY, NULL,
806 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
811 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
816 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
821 EL_EMC_MAGNIFIER, -1,
822 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
823 &li.magnify_score, 10
826 EL_EMC_MAGNIFIER, -1,
827 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
832 EL_EMC_MAGIC_BALL, -1,
833 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
837 EL_EMC_MAGIC_BALL, -1,
838 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
839 &li.ball_random, FALSE
842 EL_EMC_MAGIC_BALL, -1,
843 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
844 &li.ball_active_initial, FALSE
847 EL_EMC_MAGIC_BALL, -1,
848 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
849 &li.ball_content, EL_EMPTY, NULL,
850 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
854 EL_SOKOBAN_FIELD_EMPTY, -1,
855 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
856 &li.sb_fields_needed, TRUE
860 EL_SOKOBAN_OBJECT, -1,
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
862 &li.sb_objects_needed, TRUE
867 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
868 &li.mm_laser_red, FALSE
872 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
873 &li.mm_laser_green, FALSE
877 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
878 &li.mm_laser_blue, TRUE
883 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
884 &li.df_laser_red, TRUE
888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
889 &li.df_laser_green, TRUE
893 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
894 &li.df_laser_blue, FALSE
898 EL_MM_FUSE_ACTIVE, -1,
899 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
904 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
916 &li.mm_ball_choice_mode, ANIM_RANDOM
920 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
921 &li.mm_ball_content, EL_EMPTY, NULL,
922 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
926 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
927 &li.rotate_mm_ball_content, TRUE
931 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
932 &li.explode_mm_ball, FALSE
936 EL_MM_STEEL_BLOCK, -1,
937 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
938 &li.mm_time_block, 75
942 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
943 &li.score[SC_ELEM_BONUS], 10
946 // ---------- unused values -------------------------------------------------
949 EL_UNKNOWN, SAVE_CONF_NEVER,
950 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
951 &li.score[SC_UNKNOWN_15], 10
961 static struct LevelFileConfigInfo chunk_config_NOTE[] =
965 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
966 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
970 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
971 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
977 &xx_envelope.autowrap, FALSE
981 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
982 &xx_envelope.centered, FALSE
987 TYPE_STRING, CONF_VALUE_BYTES(1),
988 &xx_envelope.text, -1, NULL,
989 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
990 &xx_default_string_empty[0]
1000 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1004 TYPE_STRING, CONF_VALUE_BYTES(1),
1005 &xx_ei.description[0], -1,
1006 &yy_ei.description[0],
1007 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1008 &xx_default_description[0]
1013 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1014 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1015 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1017 #if ENABLE_RESERVED_CODE
1018 // (reserved for later use)
1021 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1022 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1023 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1029 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1030 &xx_ei.use_gfx_element, FALSE,
1031 &yy_ei.use_gfx_element
1035 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1036 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1037 &yy_ei.gfx_element_initial
1042 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1043 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1044 &yy_ei.access_direction
1049 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1050 &xx_ei.collect_score_initial, 10,
1051 &yy_ei.collect_score_initial
1055 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1056 &xx_ei.collect_count_initial, 1,
1057 &yy_ei.collect_count_initial
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1063 &xx_ei.ce_value_fixed_initial, 0,
1064 &yy_ei.ce_value_fixed_initial
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1069 &xx_ei.ce_value_random_initial, 0,
1070 &yy_ei.ce_value_random_initial
1074 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1075 &xx_ei.use_last_ce_value, FALSE,
1076 &yy_ei.use_last_ce_value
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1082 &xx_ei.push_delay_fixed, 8,
1083 &yy_ei.push_delay_fixed
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1088 &xx_ei.push_delay_random, 8,
1089 &yy_ei.push_delay_random
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1094 &xx_ei.drop_delay_fixed, 0,
1095 &yy_ei.drop_delay_fixed
1099 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1100 &xx_ei.drop_delay_random, 0,
1101 &yy_ei.drop_delay_random
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1106 &xx_ei.move_delay_fixed, 0,
1107 &yy_ei.move_delay_fixed
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1112 &xx_ei.move_delay_random, 0,
1113 &yy_ei.move_delay_random
1117 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1118 &xx_ei.step_delay_fixed, 0,
1119 &yy_ei.step_delay_fixed
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1124 &xx_ei.step_delay_random, 0,
1125 &yy_ei.step_delay_random
1130 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1131 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1136 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1137 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1138 &yy_ei.move_direction_initial
1142 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1143 &xx_ei.move_stepsize, TILEX / 8,
1144 &yy_ei.move_stepsize
1149 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1150 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1151 &yy_ei.move_enter_element
1155 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1156 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1157 &yy_ei.move_leave_element
1161 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1162 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1163 &yy_ei.move_leave_type
1168 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1169 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1170 &yy_ei.slippery_type
1175 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1176 &xx_ei.explosion_type, EXPLODES_3X3,
1177 &yy_ei.explosion_type
1181 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1182 &xx_ei.explosion_delay, 16,
1183 &yy_ei.explosion_delay
1187 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1188 &xx_ei.ignition_delay, 8,
1189 &yy_ei.ignition_delay
1194 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1195 &xx_ei.content, EL_EMPTY_SPACE,
1197 &xx_num_contents, 1, 1
1200 // ---------- "num_change_pages" must be the last entry ---------------------
1203 -1, SAVE_CONF_ALWAYS,
1204 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1205 &xx_ei.num_change_pages, 1,
1206 &yy_ei.num_change_pages
1217 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1219 // ---------- "current_change_page" must be the first entry -----------------
1222 -1, SAVE_CONF_ALWAYS,
1223 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1224 &xx_current_change_page, -1
1227 // ---------- (the remaining entries can be in any order) -------------------
1231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1232 &xx_change.can_change, FALSE
1237 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1238 &xx_event_bits[0], 0
1242 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1243 &xx_event_bits[1], 0
1248 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1249 &xx_change.trigger_player, CH_PLAYER_ANY
1253 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1254 &xx_change.trigger_side, CH_SIDE_ANY
1258 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1259 &xx_change.trigger_page, CH_PAGE_ANY
1264 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1265 &xx_change.target_element, EL_EMPTY_SPACE
1270 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1271 &xx_change.delay_fixed, 0
1275 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1276 &xx_change.delay_random, 0
1280 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1281 &xx_change.delay_frames, FRAMES_PER_SECOND
1286 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1287 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1293 &xx_change.explode, FALSE
1297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1298 &xx_change.use_target_content, FALSE
1302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1303 &xx_change.only_if_complete, FALSE
1307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1308 &xx_change.use_random_replace, FALSE
1312 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1313 &xx_change.random_percentage, 100
1317 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1318 &xx_change.replace_when, CP_WHEN_EMPTY
1323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1324 &xx_change.has_action, FALSE
1328 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1329 &xx_change.action_type, CA_NO_ACTION
1333 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1334 &xx_change.action_mode, CA_MODE_UNDEFINED
1338 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1339 &xx_change.action_arg, CA_ARG_UNDEFINED
1344 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1345 &xx_change.action_element, EL_EMPTY_SPACE
1350 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1351 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1352 &xx_num_contents, 1, 1
1362 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1366 TYPE_STRING, CONF_VALUE_BYTES(1),
1367 &xx_ei.description[0], -1, NULL,
1368 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1369 &xx_default_description[0]
1374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1375 &xx_ei.use_gfx_element, FALSE
1379 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1380 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1385 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1386 &xx_group.choice_mode, ANIM_RANDOM
1391 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1392 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1393 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1403 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1408 &xx_ei.use_gfx_element, FALSE
1412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1413 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1423 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1428 &li.block_snap_field, TRUE
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1433 &li.continuous_snapping, TRUE
1437 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1438 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1443 &li.use_start_element[0], FALSE
1447 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1448 &li.start_element[0], EL_PLAYER_1
1452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1453 &li.use_artwork_element[0], FALSE
1457 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1458 &li.artwork_element[0], EL_PLAYER_1
1462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1463 &li.use_explosion_element[0], FALSE
1467 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1468 &li.explosion_element[0], EL_PLAYER_1
1483 filetype_id_list[] =
1485 { LEVEL_FILE_TYPE_RND, "RND" },
1486 { LEVEL_FILE_TYPE_BD, "BD" },
1487 { LEVEL_FILE_TYPE_EM, "EM" },
1488 { LEVEL_FILE_TYPE_SP, "SP" },
1489 { LEVEL_FILE_TYPE_DX, "DX" },
1490 { LEVEL_FILE_TYPE_SB, "SB" },
1491 { LEVEL_FILE_TYPE_DC, "DC" },
1492 { LEVEL_FILE_TYPE_MM, "MM" },
1493 { LEVEL_FILE_TYPE_MM, "DF" },
1498 // ============================================================================
1499 // level file functions
1500 // ============================================================================
1502 static boolean check_special_flags(char *flag)
1504 if (strEqual(options.special_flags, flag) ||
1505 strEqual(leveldir_current->special_flags, flag))
1511 static struct DateInfo getCurrentDate(void)
1513 time_t epoch_seconds = time(NULL);
1514 struct tm *now = localtime(&epoch_seconds);
1515 struct DateInfo date;
1517 date.year = now->tm_year + 1900;
1518 date.month = now->tm_mon + 1;
1519 date.day = now->tm_mday;
1521 date.src = DATE_SRC_CLOCK;
1526 static void resetEventFlags(struct ElementChangeInfo *change)
1530 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1531 change->has_event[i] = FALSE;
1534 static void resetEventBits(void)
1538 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1539 xx_event_bits[i] = 0;
1542 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1546 /* important: only change event flag if corresponding event bit is set
1547 (this is because all xx_event_bits[] values are loaded separately,
1548 and all xx_event_bits[] values are set back to zero before loading
1549 another value xx_event_bits[x] (each value representing 32 flags)) */
1551 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1552 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1553 change->has_event[i] = TRUE;
1556 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1560 /* in contrast to the above function setEventFlagsFromEventBits(), it
1561 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1562 depending on the corresponding change->has_event[i] values here, as
1563 all xx_event_bits[] values are reset in resetEventBits() before */
1565 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1566 if (change->has_event[i])
1567 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1570 static char *getDefaultElementDescription(struct ElementInfo *ei)
1572 static char description[MAX_ELEMENT_NAME_LEN + 1];
1573 char *default_description = (ei->custom_description != NULL ?
1574 ei->custom_description :
1575 ei->editor_description);
1578 // always start with reliable default values
1579 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1580 description[i] = '\0';
1582 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1583 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1585 return &description[0];
1588 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1590 char *default_description = getDefaultElementDescription(ei);
1593 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1594 ei->description[i] = default_description[i];
1597 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1601 for (i = 0; conf[i].data_type != -1; i++)
1603 int default_value = conf[i].default_value;
1604 int data_type = conf[i].data_type;
1605 int conf_type = conf[i].conf_type;
1606 int byte_mask = conf_type & CONF_MASK_BYTES;
1608 if (byte_mask == CONF_MASK_MULTI_BYTES)
1610 int default_num_entities = conf[i].default_num_entities;
1611 int max_num_entities = conf[i].max_num_entities;
1613 *(int *)(conf[i].num_entities) = default_num_entities;
1615 if (data_type == TYPE_STRING)
1617 char *default_string = conf[i].default_string;
1618 char *string = (char *)(conf[i].value);
1620 strncpy(string, default_string, max_num_entities);
1622 else if (data_type == TYPE_ELEMENT_LIST)
1624 int *element_array = (int *)(conf[i].value);
1627 for (j = 0; j < max_num_entities; j++)
1628 element_array[j] = default_value;
1630 else if (data_type == TYPE_CONTENT_LIST)
1632 struct Content *content = (struct Content *)(conf[i].value);
1635 for (c = 0; c < max_num_entities; c++)
1636 for (y = 0; y < 3; y++)
1637 for (x = 0; x < 3; x++)
1638 content[c].e[x][y] = default_value;
1641 else // constant size configuration data (1, 2 or 4 bytes)
1643 if (data_type == TYPE_BOOLEAN)
1644 *(boolean *)(conf[i].value) = default_value;
1646 *(int *) (conf[i].value) = default_value;
1651 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1655 for (i = 0; conf[i].data_type != -1; i++)
1657 int data_type = conf[i].data_type;
1658 int conf_type = conf[i].conf_type;
1659 int byte_mask = conf_type & CONF_MASK_BYTES;
1661 if (byte_mask == CONF_MASK_MULTI_BYTES)
1663 int max_num_entities = conf[i].max_num_entities;
1665 if (data_type == TYPE_STRING)
1667 char *string = (char *)(conf[i].value);
1668 char *string_copy = (char *)(conf[i].value_copy);
1670 strncpy(string_copy, string, max_num_entities);
1672 else if (data_type == TYPE_ELEMENT_LIST)
1674 int *element_array = (int *)(conf[i].value);
1675 int *element_array_copy = (int *)(conf[i].value_copy);
1678 for (j = 0; j < max_num_entities; j++)
1679 element_array_copy[j] = element_array[j];
1681 else if (data_type == TYPE_CONTENT_LIST)
1683 struct Content *content = (struct Content *)(conf[i].value);
1684 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1687 for (c = 0; c < max_num_entities; c++)
1688 for (y = 0; y < 3; y++)
1689 for (x = 0; x < 3; x++)
1690 content_copy[c].e[x][y] = content[c].e[x][y];
1693 else // constant size configuration data (1, 2 or 4 bytes)
1695 if (data_type == TYPE_BOOLEAN)
1696 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1698 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1703 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1707 xx_ei = *ei_from; // copy element data into temporary buffer
1708 yy_ei = *ei_to; // copy element data into temporary buffer
1710 copyConfigFromConfigList(chunk_config_CUSX_base);
1715 // ---------- reinitialize and copy change pages ----------
1717 ei_to->num_change_pages = ei_from->num_change_pages;
1718 ei_to->current_change_page = ei_from->current_change_page;
1720 setElementChangePages(ei_to, ei_to->num_change_pages);
1722 for (i = 0; i < ei_to->num_change_pages; i++)
1723 ei_to->change_page[i] = ei_from->change_page[i];
1725 // ---------- copy group element info ----------
1726 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1727 *ei_to->group = *ei_from->group;
1729 // mark this custom element as modified
1730 ei_to->modified_settings = TRUE;
1733 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1735 int change_page_size = sizeof(struct ElementChangeInfo);
1737 ei->num_change_pages = MAX(1, change_pages);
1740 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1742 if (ei->current_change_page >= ei->num_change_pages)
1743 ei->current_change_page = ei->num_change_pages - 1;
1745 ei->change = &ei->change_page[ei->current_change_page];
1748 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1750 xx_change = *change; // copy change data into temporary buffer
1752 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1754 *change = xx_change;
1756 resetEventFlags(change);
1758 change->direct_action = 0;
1759 change->other_action = 0;
1761 change->pre_change_function = NULL;
1762 change->change_function = NULL;
1763 change->post_change_function = NULL;
1766 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1770 li = *level; // copy level data into temporary buffer
1771 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1772 *level = li; // copy temporary buffer back to level data
1774 setLevelInfoToDefaults_BD();
1775 setLevelInfoToDefaults_EM();
1776 setLevelInfoToDefaults_SP();
1777 setLevelInfoToDefaults_MM();
1779 level->native_bd_level = &native_bd_level;
1780 level->native_em_level = &native_em_level;
1781 level->native_sp_level = &native_sp_level;
1782 level->native_mm_level = &native_mm_level;
1784 level->file_version = FILE_VERSION_ACTUAL;
1785 level->game_version = GAME_VERSION_ACTUAL;
1787 level->creation_date = getCurrentDate();
1789 level->encoding_16bit_field = TRUE;
1790 level->encoding_16bit_yamyam = TRUE;
1791 level->encoding_16bit_amoeba = TRUE;
1793 // clear level name and level author string buffers
1794 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1795 level->name[i] = '\0';
1796 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1797 level->author[i] = '\0';
1799 // set level name and level author to default values
1800 strcpy(level->name, NAMELESS_LEVEL_NAME);
1801 strcpy(level->author, ANONYMOUS_NAME);
1803 // set level playfield to playable default level with player and exit
1804 for (x = 0; x < MAX_LEV_FIELDX; x++)
1805 for (y = 0; y < MAX_LEV_FIELDY; y++)
1806 level->field[x][y] = EL_SAND;
1808 level->field[0][0] = EL_PLAYER_1;
1809 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1811 BorderElement = EL_STEELWALL;
1813 // detect custom elements when loading them
1814 level->file_has_custom_elements = FALSE;
1816 // set all bug compatibility flags to "false" => do not emulate this bug
1817 level->use_action_after_change_bug = FALSE;
1819 if (leveldir_current)
1821 // try to determine better author name than 'anonymous'
1822 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1824 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1825 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1829 switch (LEVELCLASS(leveldir_current))
1831 case LEVELCLASS_TUTORIAL:
1832 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1835 case LEVELCLASS_CONTRIB:
1836 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1837 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1840 case LEVELCLASS_PRIVATE:
1841 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1842 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1846 // keep default value
1853 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1855 static boolean clipboard_elements_initialized = FALSE;
1858 InitElementPropertiesStatic();
1860 li = *level; // copy level data into temporary buffer
1861 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1862 *level = li; // copy temporary buffer back to level data
1864 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1867 struct ElementInfo *ei = &element_info[element];
1869 if (element == EL_MM_GRAY_BALL)
1871 struct LevelInfo_MM *level_mm = level->native_mm_level;
1874 for (j = 0; j < level->num_mm_ball_contents; j++)
1875 level->mm_ball_content[j] =
1876 map_element_MM_to_RND(level_mm->ball_content[j]);
1879 // never initialize clipboard elements after the very first time
1880 // (to be able to use clipboard elements between several levels)
1881 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1884 if (IS_ENVELOPE(element))
1886 int envelope_nr = element - EL_ENVELOPE_1;
1888 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1890 level->envelope[envelope_nr] = xx_envelope;
1893 if (IS_CUSTOM_ELEMENT(element) ||
1894 IS_GROUP_ELEMENT(element) ||
1895 IS_INTERNAL_ELEMENT(element))
1897 xx_ei = *ei; // copy element data into temporary buffer
1899 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1904 setElementChangePages(ei, 1);
1905 setElementChangeInfoToDefaults(ei->change);
1907 if (IS_CUSTOM_ELEMENT(element) ||
1908 IS_GROUP_ELEMENT(element))
1910 setElementDescriptionToDefault(ei);
1912 ei->modified_settings = FALSE;
1915 if (IS_CUSTOM_ELEMENT(element) ||
1916 IS_INTERNAL_ELEMENT(element))
1918 // internal values used in level editor
1920 ei->access_type = 0;
1921 ei->access_layer = 0;
1922 ei->access_protected = 0;
1923 ei->walk_to_action = 0;
1924 ei->smash_targets = 0;
1927 ei->can_explode_by_fire = FALSE;
1928 ei->can_explode_smashed = FALSE;
1929 ei->can_explode_impact = FALSE;
1931 ei->current_change_page = 0;
1934 if (IS_GROUP_ELEMENT(element) ||
1935 IS_INTERNAL_ELEMENT(element))
1937 struct ElementGroupInfo *group;
1939 // initialize memory for list of elements in group
1940 if (ei->group == NULL)
1941 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1945 xx_group = *group; // copy group data into temporary buffer
1947 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1952 if (IS_EMPTY_ELEMENT(element) ||
1953 IS_INTERNAL_ELEMENT(element))
1955 xx_ei = *ei; // copy element data into temporary buffer
1957 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1963 clipboard_elements_initialized = TRUE;
1966 static void setLevelInfoToDefaults(struct LevelInfo *level,
1967 boolean level_info_only,
1968 boolean reset_file_status)
1970 setLevelInfoToDefaults_Level(level);
1972 if (!level_info_only)
1973 setLevelInfoToDefaults_Elements(level);
1975 if (reset_file_status)
1977 level->no_valid_file = FALSE;
1978 level->no_level_file = FALSE;
1981 level->changed = FALSE;
1984 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1986 level_file_info->nr = 0;
1987 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1988 level_file_info->packed = FALSE;
1990 setString(&level_file_info->basename, NULL);
1991 setString(&level_file_info->filename, NULL);
1994 int getMappedElement_SB(int, boolean);
1996 static void ActivateLevelTemplate(void)
2000 if (check_special_flags("load_xsb_to_ces"))
2002 // fill smaller playfields with padding "beyond border wall" elements
2003 if (level.fieldx < level_template.fieldx ||
2004 level.fieldy < level_template.fieldy)
2006 short field[level.fieldx][level.fieldy];
2007 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2008 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2009 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2010 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2012 // copy old playfield (which is smaller than the visible area)
2013 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2014 field[x][y] = level.field[x][y];
2016 // fill new, larger playfield with "beyond border wall" elements
2017 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2018 level.field[x][y] = getMappedElement_SB('_', TRUE);
2020 // copy the old playfield to the middle of the new playfield
2021 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2022 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2024 level.fieldx = new_fieldx;
2025 level.fieldy = new_fieldy;
2029 // Currently there is no special action needed to activate the template
2030 // data, because 'element_info' property settings overwrite the original
2031 // level data, while all other variables do not change.
2033 // Exception: 'from_level_template' elements in the original level playfield
2034 // are overwritten with the corresponding elements at the same position in
2035 // playfield from the level template.
2037 for (x = 0; x < level.fieldx; x++)
2038 for (y = 0; y < level.fieldy; y++)
2039 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2040 level.field[x][y] = level_template.field[x][y];
2042 if (check_special_flags("load_xsb_to_ces"))
2044 struct LevelInfo level_backup = level;
2046 // overwrite all individual level settings from template level settings
2047 level = level_template;
2049 // restore level file info
2050 level.file_info = level_backup.file_info;
2052 // restore playfield size
2053 level.fieldx = level_backup.fieldx;
2054 level.fieldy = level_backup.fieldy;
2056 // restore playfield content
2057 for (x = 0; x < level.fieldx; x++)
2058 for (y = 0; y < level.fieldy; y++)
2059 level.field[x][y] = level_backup.field[x][y];
2061 // restore name and author from individual level
2062 strcpy(level.name, level_backup.name);
2063 strcpy(level.author, level_backup.author);
2065 // restore flag "use_custom_template"
2066 level.use_custom_template = level_backup.use_custom_template;
2070 static char *getLevelFilenameFromBasename(char *basename)
2072 static char *filename = NULL;
2074 checked_free(filename);
2076 filename = getPath2(getCurrentLevelDir(), basename);
2081 static int getFileTypeFromBasename(char *basename)
2083 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2085 static char *filename = NULL;
2086 struct stat file_status;
2088 // ---------- try to determine file type from filename ----------
2090 // check for typical filename of a Supaplex level package file
2091 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2092 return LEVEL_FILE_TYPE_SP;
2094 // check for typical filename of a Diamond Caves II level package file
2095 if (strSuffixLower(basename, ".dc") ||
2096 strSuffixLower(basename, ".dc2"))
2097 return LEVEL_FILE_TYPE_DC;
2099 // check for typical filename of a Sokoban level package file
2100 if (strSuffixLower(basename, ".xsb") &&
2101 strchr(basename, '%') == NULL)
2102 return LEVEL_FILE_TYPE_SB;
2104 // ---------- try to determine file type from filesize ----------
2106 checked_free(filename);
2107 filename = getPath2(getCurrentLevelDir(), basename);
2109 if (stat(filename, &file_status) == 0)
2111 // check for typical filesize of a Supaplex level package file
2112 if (file_status.st_size == 170496)
2113 return LEVEL_FILE_TYPE_SP;
2116 return LEVEL_FILE_TYPE_UNKNOWN;
2119 static int getFileTypeFromMagicBytes(char *filename, int type)
2123 if ((file = openFile(filename, MODE_READ)))
2125 char chunk_name[CHUNK_ID_LEN + 1];
2127 getFileChunkBE(file, chunk_name, NULL);
2129 if (strEqual(chunk_name, "MMII") ||
2130 strEqual(chunk_name, "MIRR"))
2131 type = LEVEL_FILE_TYPE_MM;
2139 static boolean checkForPackageFromBasename(char *basename)
2141 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2142 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2144 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2147 static char *getSingleLevelBasenameExt(int nr, char *extension)
2149 static char basename[MAX_FILENAME_LEN];
2152 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2154 sprintf(basename, "%03d.%s", nr, extension);
2159 static char *getSingleLevelBasename(int nr)
2161 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2164 static char *getPackedLevelBasename(int type)
2166 static char basename[MAX_FILENAME_LEN];
2167 char *directory = getCurrentLevelDir();
2169 DirectoryEntry *dir_entry;
2171 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2173 if ((dir = openDirectory(directory)) == NULL)
2175 Warn("cannot read current level directory '%s'", directory);
2180 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2182 char *entry_basename = dir_entry->basename;
2183 int entry_type = getFileTypeFromBasename(entry_basename);
2185 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2187 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2190 strcpy(basename, entry_basename);
2197 closeDirectory(dir);
2202 static char *getSingleLevelFilename(int nr)
2204 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2207 #if ENABLE_UNUSED_CODE
2208 static char *getPackedLevelFilename(int type)
2210 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2214 char *getDefaultLevelFilename(int nr)
2216 return getSingleLevelFilename(nr);
2219 #if ENABLE_UNUSED_CODE
2220 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2224 lfi->packed = FALSE;
2226 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2227 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2231 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2232 int type, char *format, ...)
2234 static char basename[MAX_FILENAME_LEN];
2237 va_start(ap, format);
2238 vsprintf(basename, format, ap);
2242 lfi->packed = FALSE;
2244 setString(&lfi->basename, basename);
2245 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2248 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2254 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2255 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2258 static int getFiletypeFromID(char *filetype_id)
2260 char *filetype_id_lower;
2261 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2264 if (filetype_id == NULL)
2265 return LEVEL_FILE_TYPE_UNKNOWN;
2267 filetype_id_lower = getStringToLower(filetype_id);
2269 for (i = 0; filetype_id_list[i].id != NULL; i++)
2271 char *id_lower = getStringToLower(filetype_id_list[i].id);
2273 if (strEqual(filetype_id_lower, id_lower))
2274 filetype = filetype_id_list[i].filetype;
2278 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2282 free(filetype_id_lower);
2287 char *getLocalLevelTemplateFilename(void)
2289 return getDefaultLevelFilename(-1);
2292 char *getGlobalLevelTemplateFilename(void)
2294 // global variable "leveldir_current" must be modified in the loop below
2295 LevelDirTree *leveldir_current_last = leveldir_current;
2296 char *filename = NULL;
2298 // check for template level in path from current to topmost tree node
2300 while (leveldir_current != NULL)
2302 filename = getDefaultLevelFilename(-1);
2304 if (fileExists(filename))
2307 leveldir_current = leveldir_current->node_parent;
2310 // restore global variable "leveldir_current" modified in above loop
2311 leveldir_current = leveldir_current_last;
2316 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2320 // special case: level number is negative => check for level template file
2323 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2324 getSingleLevelBasename(-1));
2326 // replace local level template filename with global template filename
2327 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2329 // no fallback if template file not existing
2333 // special case: check for file name/pattern specified in "levelinfo.conf"
2334 if (leveldir_current->level_filename != NULL)
2336 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2338 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2339 leveldir_current->level_filename, nr);
2341 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2343 if (fileExists(lfi->filename))
2346 else if (leveldir_current->level_filetype != NULL)
2348 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2350 // check for specified native level file with standard file name
2351 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2352 "%03d.%s", nr, LEVELFILE_EXTENSION);
2353 if (fileExists(lfi->filename))
2357 // check for native Rocks'n'Diamonds level file
2358 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2359 "%03d.%s", nr, LEVELFILE_EXTENSION);
2360 if (fileExists(lfi->filename))
2363 // check for Emerald Mine level file (V1)
2364 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2365 'a' + (nr / 10) % 26, '0' + nr % 10);
2366 if (fileExists(lfi->filename))
2368 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2369 'A' + (nr / 10) % 26, '0' + nr % 10);
2370 if (fileExists(lfi->filename))
2373 // check for Emerald Mine level file (V2 to V5)
2374 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2375 if (fileExists(lfi->filename))
2378 // check for Emerald Mine level file (V6 / single mode)
2379 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2380 if (fileExists(lfi->filename))
2382 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2383 if (fileExists(lfi->filename))
2386 // check for Emerald Mine level file (V6 / teamwork mode)
2387 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2388 if (fileExists(lfi->filename))
2390 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2391 if (fileExists(lfi->filename))
2394 // check for various packed level file formats
2395 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2396 if (fileExists(lfi->filename))
2399 // no known level file found -- use default values (and fail later)
2400 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2401 "%03d.%s", nr, LEVELFILE_EXTENSION);
2404 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2406 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2407 lfi->type = getFileTypeFromBasename(lfi->basename);
2409 if (lfi->type == LEVEL_FILE_TYPE_RND)
2410 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2413 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2415 // always start with reliable default values
2416 setFileInfoToDefaults(level_file_info);
2418 level_file_info->nr = nr; // set requested level number
2420 determineLevelFileInfo_Filename(level_file_info);
2421 determineLevelFileInfo_Filetype(level_file_info);
2424 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2425 struct LevelFileInfo *lfi_to)
2427 lfi_to->nr = lfi_from->nr;
2428 lfi_to->type = lfi_from->type;
2429 lfi_to->packed = lfi_from->packed;
2431 setString(&lfi_to->basename, lfi_from->basename);
2432 setString(&lfi_to->filename, lfi_from->filename);
2435 // ----------------------------------------------------------------------------
2436 // functions for loading R'n'D level
2437 // ----------------------------------------------------------------------------
2439 int getMappedElement(int element)
2441 // remap some (historic, now obsolete) elements
2445 case EL_PLAYER_OBSOLETE:
2446 element = EL_PLAYER_1;
2449 case EL_KEY_OBSOLETE:
2453 case EL_EM_KEY_1_FILE_OBSOLETE:
2454 element = EL_EM_KEY_1;
2457 case EL_EM_KEY_2_FILE_OBSOLETE:
2458 element = EL_EM_KEY_2;
2461 case EL_EM_KEY_3_FILE_OBSOLETE:
2462 element = EL_EM_KEY_3;
2465 case EL_EM_KEY_4_FILE_OBSOLETE:
2466 element = EL_EM_KEY_4;
2469 case EL_ENVELOPE_OBSOLETE:
2470 element = EL_ENVELOPE_1;
2478 if (element >= NUM_FILE_ELEMENTS)
2480 Warn("invalid level element %d", element);
2482 element = EL_UNKNOWN;
2490 static int getMappedElementByVersion(int element, int game_version)
2492 // remap some elements due to certain game version
2494 if (game_version <= VERSION_IDENT(2,2,0,0))
2496 // map game font elements
2497 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2498 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2499 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2500 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2503 if (game_version < VERSION_IDENT(3,0,0,0))
2505 // map Supaplex gravity tube elements
2506 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2507 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2508 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2509 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2516 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2518 level->file_version = getFileVersion(file);
2519 level->game_version = getFileVersion(file);
2524 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2526 level->creation_date.year = getFile16BitBE(file);
2527 level->creation_date.month = getFile8Bit(file);
2528 level->creation_date.day = getFile8Bit(file);
2530 level->creation_date.src = DATE_SRC_LEVELFILE;
2535 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2537 int initial_player_stepsize;
2538 int initial_player_gravity;
2541 level->fieldx = getFile8Bit(file);
2542 level->fieldy = getFile8Bit(file);
2544 level->time = getFile16BitBE(file);
2545 level->gems_needed = getFile16BitBE(file);
2547 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2548 level->name[i] = getFile8Bit(file);
2549 level->name[MAX_LEVEL_NAME_LEN] = 0;
2551 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2552 level->score[i] = getFile8Bit(file);
2554 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2555 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2556 for (y = 0; y < 3; y++)
2557 for (x = 0; x < 3; x++)
2558 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2560 level->amoeba_speed = getFile8Bit(file);
2561 level->time_magic_wall = getFile8Bit(file);
2562 level->time_wheel = getFile8Bit(file);
2563 level->amoeba_content = getMappedElement(getFile8Bit(file));
2565 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2568 for (i = 0; i < MAX_PLAYERS; i++)
2569 level->initial_player_stepsize[i] = initial_player_stepsize;
2571 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2573 for (i = 0; i < MAX_PLAYERS; i++)
2574 level->initial_player_gravity[i] = initial_player_gravity;
2576 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2577 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2579 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2581 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2582 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2583 level->can_move_into_acid_bits = getFile32BitBE(file);
2584 level->dont_collide_with_bits = getFile8Bit(file);
2586 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2587 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2589 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2590 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2591 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2593 level->game_engine_type = getFile8Bit(file);
2595 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2600 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2604 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2605 level->name[i] = getFile8Bit(file);
2606 level->name[MAX_LEVEL_NAME_LEN] = 0;
2611 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2615 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2616 level->author[i] = getFile8Bit(file);
2617 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2622 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2625 int chunk_size_expected = level->fieldx * level->fieldy;
2627 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2628 stored with 16-bit encoding (and should be twice as big then).
2629 Even worse, playfield data was stored 16-bit when only yamyam content
2630 contained 16-bit elements and vice versa. */
2632 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2633 chunk_size_expected *= 2;
2635 if (chunk_size_expected != chunk_size)
2637 ReadUnusedBytesFromFile(file, chunk_size);
2638 return chunk_size_expected;
2641 for (y = 0; y < level->fieldy; y++)
2642 for (x = 0; x < level->fieldx; x++)
2643 level->field[x][y] =
2644 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2649 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2652 int header_size = 4;
2653 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2654 int chunk_size_expected = header_size + content_size;
2656 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2657 stored with 16-bit encoding (and should be twice as big then).
2658 Even worse, playfield data was stored 16-bit when only yamyam content
2659 contained 16-bit elements and vice versa. */
2661 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2662 chunk_size_expected += content_size;
2664 if (chunk_size_expected != chunk_size)
2666 ReadUnusedBytesFromFile(file, chunk_size);
2667 return chunk_size_expected;
2671 level->num_yamyam_contents = getFile8Bit(file);
2675 // correct invalid number of content fields -- should never happen
2676 if (level->num_yamyam_contents < 1 ||
2677 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2678 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2680 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2681 for (y = 0; y < 3; y++)
2682 for (x = 0; x < 3; x++)
2683 level->yamyam_content[i].e[x][y] =
2684 getMappedElement(level->encoding_16bit_field ?
2685 getFile16BitBE(file) : getFile8Bit(file));
2689 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2694 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2696 element = getMappedElement(getFile16BitBE(file));
2697 num_contents = getFile8Bit(file);
2699 getFile8Bit(file); // content x size (unused)
2700 getFile8Bit(file); // content y size (unused)
2702 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2704 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2705 for (y = 0; y < 3; y++)
2706 for (x = 0; x < 3; x++)
2707 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2709 // correct invalid number of content fields -- should never happen
2710 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2711 num_contents = STD_ELEMENT_CONTENTS;
2713 if (element == EL_YAMYAM)
2715 level->num_yamyam_contents = num_contents;
2717 for (i = 0; i < num_contents; i++)
2718 for (y = 0; y < 3; y++)
2719 for (x = 0; x < 3; x++)
2720 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2722 else if (element == EL_BD_AMOEBA)
2724 level->amoeba_content = content_array[0][0][0];
2728 Warn("cannot load content for element '%d'", element);
2734 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2740 int chunk_size_expected;
2742 element = getMappedElement(getFile16BitBE(file));
2743 if (!IS_ENVELOPE(element))
2744 element = EL_ENVELOPE_1;
2746 envelope_nr = element - EL_ENVELOPE_1;
2748 envelope_len = getFile16BitBE(file);
2750 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2751 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2753 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2755 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2756 if (chunk_size_expected != chunk_size)
2758 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2759 return chunk_size_expected;
2762 for (i = 0; i < envelope_len; i++)
2763 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2768 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2770 int num_changed_custom_elements = getFile16BitBE(file);
2771 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2774 if (chunk_size_expected != chunk_size)
2776 ReadUnusedBytesFromFile(file, chunk_size - 2);
2777 return chunk_size_expected;
2780 for (i = 0; i < num_changed_custom_elements; i++)
2782 int element = getMappedElement(getFile16BitBE(file));
2783 int properties = getFile32BitBE(file);
2785 if (IS_CUSTOM_ELEMENT(element))
2786 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2788 Warn("invalid custom element number %d", element);
2790 // older game versions that wrote level files with CUS1 chunks used
2791 // different default push delay values (not yet stored in level file)
2792 element_info[element].push_delay_fixed = 2;
2793 element_info[element].push_delay_random = 8;
2796 level->file_has_custom_elements = TRUE;
2801 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2803 int num_changed_custom_elements = getFile16BitBE(file);
2804 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2807 if (chunk_size_expected != chunk_size)
2809 ReadUnusedBytesFromFile(file, chunk_size - 2);
2810 return chunk_size_expected;
2813 for (i = 0; i < num_changed_custom_elements; i++)
2815 int element = getMappedElement(getFile16BitBE(file));
2816 int custom_target_element = getMappedElement(getFile16BitBE(file));
2818 if (IS_CUSTOM_ELEMENT(element))
2819 element_info[element].change->target_element = custom_target_element;
2821 Warn("invalid custom element number %d", element);
2824 level->file_has_custom_elements = TRUE;
2829 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2831 int num_changed_custom_elements = getFile16BitBE(file);
2832 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2835 if (chunk_size_expected != chunk_size)
2837 ReadUnusedBytesFromFile(file, chunk_size - 2);
2838 return chunk_size_expected;
2841 for (i = 0; i < num_changed_custom_elements; i++)
2843 int element = getMappedElement(getFile16BitBE(file));
2844 struct ElementInfo *ei = &element_info[element];
2845 unsigned int event_bits;
2847 if (!IS_CUSTOM_ELEMENT(element))
2849 Warn("invalid custom element number %d", element);
2851 element = EL_INTERNAL_DUMMY;
2854 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2855 ei->description[j] = getFile8Bit(file);
2856 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2858 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2860 // some free bytes for future properties and padding
2861 ReadUnusedBytesFromFile(file, 7);
2863 ei->use_gfx_element = getFile8Bit(file);
2864 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2866 ei->collect_score_initial = getFile8Bit(file);
2867 ei->collect_count_initial = getFile8Bit(file);
2869 ei->push_delay_fixed = getFile16BitBE(file);
2870 ei->push_delay_random = getFile16BitBE(file);
2871 ei->move_delay_fixed = getFile16BitBE(file);
2872 ei->move_delay_random = getFile16BitBE(file);
2874 ei->move_pattern = getFile16BitBE(file);
2875 ei->move_direction_initial = getFile8Bit(file);
2876 ei->move_stepsize = getFile8Bit(file);
2878 for (y = 0; y < 3; y++)
2879 for (x = 0; x < 3; x++)
2880 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2882 // bits 0 - 31 of "has_event[]"
2883 event_bits = getFile32BitBE(file);
2884 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2885 if (event_bits & (1u << j))
2886 ei->change->has_event[j] = TRUE;
2888 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2890 ei->change->delay_fixed = getFile16BitBE(file);
2891 ei->change->delay_random = getFile16BitBE(file);
2892 ei->change->delay_frames = getFile16BitBE(file);
2894 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2896 ei->change->explode = getFile8Bit(file);
2897 ei->change->use_target_content = getFile8Bit(file);
2898 ei->change->only_if_complete = getFile8Bit(file);
2899 ei->change->use_random_replace = getFile8Bit(file);
2901 ei->change->random_percentage = getFile8Bit(file);
2902 ei->change->replace_when = getFile8Bit(file);
2904 for (y = 0; y < 3; y++)
2905 for (x = 0; x < 3; x++)
2906 ei->change->target_content.e[x][y] =
2907 getMappedElement(getFile16BitBE(file));
2909 ei->slippery_type = getFile8Bit(file);
2911 // some free bytes for future properties and padding
2912 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2914 // mark that this custom element has been modified
2915 ei->modified_settings = TRUE;
2918 level->file_has_custom_elements = TRUE;
2923 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2925 struct ElementInfo *ei;
2926 int chunk_size_expected;
2930 // ---------- custom element base property values (96 bytes) ----------------
2932 element = getMappedElement(getFile16BitBE(file));
2934 if (!IS_CUSTOM_ELEMENT(element))
2936 Warn("invalid custom element number %d", element);
2938 ReadUnusedBytesFromFile(file, chunk_size - 2);
2943 ei = &element_info[element];
2945 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2946 ei->description[i] = getFile8Bit(file);
2947 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2949 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2951 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2953 ei->num_change_pages = getFile8Bit(file);
2955 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2956 if (chunk_size_expected != chunk_size)
2958 ReadUnusedBytesFromFile(file, chunk_size - 43);
2959 return chunk_size_expected;
2962 ei->ce_value_fixed_initial = getFile16BitBE(file);
2963 ei->ce_value_random_initial = getFile16BitBE(file);
2964 ei->use_last_ce_value = getFile8Bit(file);
2966 ei->use_gfx_element = getFile8Bit(file);
2967 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2969 ei->collect_score_initial = getFile8Bit(file);
2970 ei->collect_count_initial = getFile8Bit(file);
2972 ei->drop_delay_fixed = getFile8Bit(file);
2973 ei->push_delay_fixed = getFile8Bit(file);
2974 ei->drop_delay_random = getFile8Bit(file);
2975 ei->push_delay_random = getFile8Bit(file);
2976 ei->move_delay_fixed = getFile16BitBE(file);
2977 ei->move_delay_random = getFile16BitBE(file);
2979 // bits 0 - 15 of "move_pattern" ...
2980 ei->move_pattern = getFile16BitBE(file);
2981 ei->move_direction_initial = getFile8Bit(file);
2982 ei->move_stepsize = getFile8Bit(file);
2984 ei->slippery_type = getFile8Bit(file);
2986 for (y = 0; y < 3; y++)
2987 for (x = 0; x < 3; x++)
2988 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2990 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2991 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2992 ei->move_leave_type = getFile8Bit(file);
2994 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2995 ei->move_pattern |= (getFile16BitBE(file) << 16);
2997 ei->access_direction = getFile8Bit(file);
2999 ei->explosion_delay = getFile8Bit(file);
3000 ei->ignition_delay = getFile8Bit(file);
3001 ei->explosion_type = getFile8Bit(file);
3003 // some free bytes for future custom property values and padding
3004 ReadUnusedBytesFromFile(file, 1);
3006 // ---------- change page property values (48 bytes) ------------------------
3008 setElementChangePages(ei, ei->num_change_pages);
3010 for (i = 0; i < ei->num_change_pages; i++)
3012 struct ElementChangeInfo *change = &ei->change_page[i];
3013 unsigned int event_bits;
3015 // always start with reliable default values
3016 setElementChangeInfoToDefaults(change);
3018 // bits 0 - 31 of "has_event[]" ...
3019 event_bits = getFile32BitBE(file);
3020 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3021 if (event_bits & (1u << j))
3022 change->has_event[j] = TRUE;
3024 change->target_element = getMappedElement(getFile16BitBE(file));
3026 change->delay_fixed = getFile16BitBE(file);
3027 change->delay_random = getFile16BitBE(file);
3028 change->delay_frames = getFile16BitBE(file);
3030 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3032 change->explode = getFile8Bit(file);
3033 change->use_target_content = getFile8Bit(file);
3034 change->only_if_complete = getFile8Bit(file);
3035 change->use_random_replace = getFile8Bit(file);
3037 change->random_percentage = getFile8Bit(file);
3038 change->replace_when = getFile8Bit(file);
3040 for (y = 0; y < 3; y++)
3041 for (x = 0; x < 3; x++)
3042 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3044 change->can_change = getFile8Bit(file);
3046 change->trigger_side = getFile8Bit(file);
3048 change->trigger_player = getFile8Bit(file);
3049 change->trigger_page = getFile8Bit(file);
3051 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3052 CH_PAGE_ANY : (1 << change->trigger_page));
3054 change->has_action = getFile8Bit(file);
3055 change->action_type = getFile8Bit(file);
3056 change->action_mode = getFile8Bit(file);
3057 change->action_arg = getFile16BitBE(file);
3059 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3060 event_bits = getFile8Bit(file);
3061 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3062 if (event_bits & (1u << (j - 32)))
3063 change->has_event[j] = TRUE;
3066 // mark this custom element as modified
3067 ei->modified_settings = TRUE;
3069 level->file_has_custom_elements = TRUE;
3074 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3076 struct ElementInfo *ei;
3077 struct ElementGroupInfo *group;
3081 element = getMappedElement(getFile16BitBE(file));
3083 if (!IS_GROUP_ELEMENT(element))
3085 Warn("invalid group element number %d", element);
3087 ReadUnusedBytesFromFile(file, chunk_size - 2);
3092 ei = &element_info[element];
3094 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3095 ei->description[i] = getFile8Bit(file);
3096 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3098 group = element_info[element].group;
3100 group->num_elements = getFile8Bit(file);
3102 ei->use_gfx_element = getFile8Bit(file);
3103 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3105 group->choice_mode = getFile8Bit(file);
3107 // some free bytes for future values and padding
3108 ReadUnusedBytesFromFile(file, 3);
3110 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3111 group->element[i] = getMappedElement(getFile16BitBE(file));
3113 // mark this group element as modified
3114 element_info[element].modified_settings = TRUE;
3116 level->file_has_custom_elements = TRUE;
3121 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3122 int element, int real_element)
3124 int micro_chunk_size = 0;
3125 int conf_type = getFile8Bit(file);
3126 int byte_mask = conf_type & CONF_MASK_BYTES;
3127 boolean element_found = FALSE;
3130 micro_chunk_size += 1;
3132 if (byte_mask == CONF_MASK_MULTI_BYTES)
3134 int num_bytes = getFile16BitBE(file);
3135 byte *buffer = checked_malloc(num_bytes);
3137 ReadBytesFromFile(file, buffer, num_bytes);
3139 for (i = 0; conf[i].data_type != -1; i++)
3141 if (conf[i].element == element &&
3142 conf[i].conf_type == conf_type)
3144 int data_type = conf[i].data_type;
3145 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3146 int max_num_entities = conf[i].max_num_entities;
3148 if (num_entities > max_num_entities)
3150 Warn("truncating number of entities for element %d from %d to %d",
3151 element, num_entities, max_num_entities);
3153 num_entities = max_num_entities;
3156 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3157 data_type == TYPE_CONTENT_LIST))
3159 // for element and content lists, zero entities are not allowed
3160 Warn("found empty list of entities for element %d", element);
3162 // do not set "num_entities" here to prevent reading behind buffer
3164 *(int *)(conf[i].num_entities) = 1; // at least one is required
3168 *(int *)(conf[i].num_entities) = num_entities;
3171 element_found = TRUE;
3173 if (data_type == TYPE_STRING)
3175 char *string = (char *)(conf[i].value);
3178 for (j = 0; j < max_num_entities; j++)
3179 string[j] = (j < num_entities ? buffer[j] : '\0');
3181 else if (data_type == TYPE_ELEMENT_LIST)
3183 int *element_array = (int *)(conf[i].value);
3186 for (j = 0; j < num_entities; j++)
3188 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3190 else if (data_type == TYPE_CONTENT_LIST)
3192 struct Content *content= (struct Content *)(conf[i].value);
3195 for (c = 0; c < num_entities; c++)
3196 for (y = 0; y < 3; y++)
3197 for (x = 0; x < 3; x++)
3198 content[c].e[x][y] =
3199 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3202 element_found = FALSE;
3208 checked_free(buffer);
3210 micro_chunk_size += 2 + num_bytes;
3212 else // constant size configuration data (1, 2 or 4 bytes)
3214 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3215 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3216 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3218 for (i = 0; conf[i].data_type != -1; i++)
3220 if (conf[i].element == element &&
3221 conf[i].conf_type == conf_type)
3223 int data_type = conf[i].data_type;
3225 if (data_type == TYPE_ELEMENT)
3226 value = getMappedElement(value);
3228 if (data_type == TYPE_BOOLEAN)
3229 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3231 *(int *) (conf[i].value) = value;
3233 element_found = TRUE;
3239 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3244 char *error_conf_chunk_bytes =
3245 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3246 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3247 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3248 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3249 int error_element = real_element;
3251 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3252 error_conf_chunk_bytes, error_conf_chunk_token,
3253 error_element, EL_NAME(error_element));
3256 return micro_chunk_size;
3259 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3261 int real_chunk_size = 0;
3263 li = *level; // copy level data into temporary buffer
3265 while (!checkEndOfFile(file))
3267 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3269 if (real_chunk_size >= chunk_size)
3273 *level = li; // copy temporary buffer back to level data
3275 return real_chunk_size;
3278 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3280 int real_chunk_size = 0;
3282 li = *level; // copy level data into temporary buffer
3284 while (!checkEndOfFile(file))
3286 int element = getMappedElement(getFile16BitBE(file));
3288 real_chunk_size += 2;
3289 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3291 if (real_chunk_size >= chunk_size)
3295 *level = li; // copy temporary buffer back to level data
3297 return real_chunk_size;
3300 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3302 int real_chunk_size = 0;
3304 li = *level; // copy level data into temporary buffer
3306 while (!checkEndOfFile(file))
3308 int element = getMappedElement(getFile16BitBE(file));
3310 real_chunk_size += 2;
3311 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3313 if (real_chunk_size >= chunk_size)
3317 *level = li; // copy temporary buffer back to level data
3319 return real_chunk_size;
3322 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3324 int element = getMappedElement(getFile16BitBE(file));
3325 int envelope_nr = element - EL_ENVELOPE_1;
3326 int real_chunk_size = 2;
3328 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3330 while (!checkEndOfFile(file))
3332 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3335 if (real_chunk_size >= chunk_size)
3339 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3341 return real_chunk_size;
3344 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3346 int element = getMappedElement(getFile16BitBE(file));
3347 int real_chunk_size = 2;
3348 struct ElementInfo *ei = &element_info[element];
3351 xx_ei = *ei; // copy element data into temporary buffer
3353 xx_ei.num_change_pages = -1;
3355 while (!checkEndOfFile(file))
3357 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3359 if (xx_ei.num_change_pages != -1)
3362 if (real_chunk_size >= chunk_size)
3368 if (ei->num_change_pages == -1)
3370 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3373 ei->num_change_pages = 1;
3375 setElementChangePages(ei, 1);
3376 setElementChangeInfoToDefaults(ei->change);
3378 return real_chunk_size;
3381 // initialize number of change pages stored for this custom element
3382 setElementChangePages(ei, ei->num_change_pages);
3383 for (i = 0; i < ei->num_change_pages; i++)
3384 setElementChangeInfoToDefaults(&ei->change_page[i]);
3386 // start with reading properties for the first change page
3387 xx_current_change_page = 0;
3389 while (!checkEndOfFile(file))
3391 // level file might contain invalid change page number
3392 if (xx_current_change_page >= ei->num_change_pages)
3395 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3397 xx_change = *change; // copy change data into temporary buffer
3399 resetEventBits(); // reset bits; change page might have changed
3401 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3404 *change = xx_change;
3406 setEventFlagsFromEventBits(change);
3408 if (real_chunk_size >= chunk_size)
3412 level->file_has_custom_elements = TRUE;
3414 return real_chunk_size;
3417 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3419 int element = getMappedElement(getFile16BitBE(file));
3420 int real_chunk_size = 2;
3421 struct ElementInfo *ei = &element_info[element];
3422 struct ElementGroupInfo *group = ei->group;
3427 xx_ei = *ei; // copy element data into temporary buffer
3428 xx_group = *group; // copy group data into temporary buffer
3430 while (!checkEndOfFile(file))
3432 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3435 if (real_chunk_size >= chunk_size)
3442 level->file_has_custom_elements = TRUE;
3444 return real_chunk_size;
3447 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3449 int element = getMappedElement(getFile16BitBE(file));
3450 int real_chunk_size = 2;
3451 struct ElementInfo *ei = &element_info[element];
3453 xx_ei = *ei; // copy element data into temporary buffer
3455 while (!checkEndOfFile(file))
3457 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3460 if (real_chunk_size >= chunk_size)
3466 level->file_has_custom_elements = TRUE;
3468 return real_chunk_size;
3471 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3472 struct LevelFileInfo *level_file_info,
3473 boolean level_info_only)
3475 char *filename = level_file_info->filename;
3476 char cookie[MAX_LINE_LEN];
3477 char chunk_name[CHUNK_ID_LEN + 1];
3481 if (!(file = openFile(filename, MODE_READ)))
3483 level->no_valid_file = TRUE;
3484 level->no_level_file = TRUE;
3486 if (level_info_only)
3489 Warn("cannot read level '%s' -- using empty level", filename);
3491 if (!setup.editor.use_template_for_new_levels)
3494 // if level file not found, try to initialize level data from template
3495 filename = getGlobalLevelTemplateFilename();
3497 if (!(file = openFile(filename, MODE_READ)))
3500 // default: for empty levels, use level template for custom elements
3501 level->use_custom_template = TRUE;
3503 level->no_valid_file = FALSE;
3506 getFileChunkBE(file, chunk_name, NULL);
3507 if (strEqual(chunk_name, "RND1"))
3509 getFile32BitBE(file); // not used
3511 getFileChunkBE(file, chunk_name, NULL);
3512 if (!strEqual(chunk_name, "CAVE"))
3514 level->no_valid_file = TRUE;
3516 Warn("unknown format of level file '%s'", filename);
3523 else // check for pre-2.0 file format with cookie string
3525 strcpy(cookie, chunk_name);
3526 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3528 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3529 cookie[strlen(cookie) - 1] = '\0';
3531 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3533 level->no_valid_file = TRUE;
3535 Warn("unknown format of level file '%s'", filename);
3542 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3544 level->no_valid_file = TRUE;
3546 Warn("unsupported version of level file '%s'", filename);
3553 // pre-2.0 level files have no game version, so use file version here
3554 level->game_version = level->file_version;
3557 if (level->file_version < FILE_VERSION_1_2)
3559 // level files from versions before 1.2.0 without chunk structure
3560 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3561 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3569 int (*loader)(File *, int, struct LevelInfo *);
3573 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3574 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3575 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3576 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3577 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3578 { "INFO", -1, LoadLevel_INFO },
3579 { "BODY", -1, LoadLevel_BODY },
3580 { "CONT", -1, LoadLevel_CONT },
3581 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3582 { "CNT3", -1, LoadLevel_CNT3 },
3583 { "CUS1", -1, LoadLevel_CUS1 },
3584 { "CUS2", -1, LoadLevel_CUS2 },
3585 { "CUS3", -1, LoadLevel_CUS3 },
3586 { "CUS4", -1, LoadLevel_CUS4 },
3587 { "GRP1", -1, LoadLevel_GRP1 },
3588 { "CONF", -1, LoadLevel_CONF },
3589 { "ELEM", -1, LoadLevel_ELEM },
3590 { "NOTE", -1, LoadLevel_NOTE },
3591 { "CUSX", -1, LoadLevel_CUSX },
3592 { "GRPX", -1, LoadLevel_GRPX },
3593 { "EMPX", -1, LoadLevel_EMPX },
3598 while (getFileChunkBE(file, chunk_name, &chunk_size))
3602 while (chunk_info[i].name != NULL &&
3603 !strEqual(chunk_name, chunk_info[i].name))
3606 if (chunk_info[i].name == NULL)
3608 Warn("unknown chunk '%s' in level file '%s'",
3609 chunk_name, filename);
3611 ReadUnusedBytesFromFile(file, chunk_size);
3613 else if (chunk_info[i].size != -1 &&
3614 chunk_info[i].size != chunk_size)
3616 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3617 chunk_size, chunk_name, filename);
3619 ReadUnusedBytesFromFile(file, chunk_size);
3623 // call function to load this level chunk
3624 int chunk_size_expected =
3625 (chunk_info[i].loader)(file, chunk_size, level);
3627 if (chunk_size_expected < 0)
3629 Warn("error reading chunk '%s' in level file '%s'",
3630 chunk_name, filename);
3635 // the size of some chunks cannot be checked before reading other
3636 // chunks first (like "HEAD" and "BODY") that contain some header
3637 // information, so check them here
3638 if (chunk_size_expected != chunk_size)
3640 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3641 chunk_size, chunk_name, filename);
3653 // ----------------------------------------------------------------------------
3654 // functions for loading BD level
3655 // ----------------------------------------------------------------------------
3657 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3659 struct LevelInfo_BD *level_bd = level->native_bd_level;
3660 GdCave *cave = NULL; // will be changed below
3661 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3662 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3665 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3667 // cave and map newly allocated when set to defaults above
3668 cave = level_bd->cave;
3670 for (i = 0; i < 5; i++)
3672 cave->level_time[i] = level->time;
3673 cave->level_diamonds[i] = level->gems_needed;
3674 cave->level_magic_wall_time[i] = level->time_magic_wall;
3675 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3678 cave->diamond_value = level->score[SC_DIAMOND];
3679 cave->extra_diamond_value = level->score[SC_DIAMOND];
3681 cave->level_speed[0] = 160; // set cave speed
3683 strncpy(cave->name, level->name, sizeof(GdString));
3684 cave->name[sizeof(GdString) - 1] = '\0';
3686 for (x = 0; x < cave->w; x++)
3687 for (y = 0; y < cave->h; y++)
3688 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3691 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3693 struct LevelInfo_BD *level_bd = level->native_bd_level;
3694 GdCave *cave = level_bd->cave;
3695 int bd_level_nr = level_bd->level_nr;
3698 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3699 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3701 level->time = cave->level_time[bd_level_nr];
3702 level->gems_needed = cave->level_diamonds[bd_level_nr];
3703 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3705 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3706 level->score[SC_DIAMOND] = cave->diamond_value;
3708 strncpy(level->name, cave->name, MAX_LEVEL_NAME_LEN);
3709 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3711 for (x = 0; x < level->fieldx; x++)
3712 for (y = 0; y < level->fieldy; y++)
3713 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3716 static void setTapeInfoToDefaults(void);
3718 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3720 struct LevelInfo_BD *level_bd = level->native_bd_level;
3721 GdCave *cave = level_bd->cave;
3722 GdReplay *replay = level_bd->replay;
3728 // always start with reliable default values
3729 setTapeInfoToDefaults();
3731 tape.level_nr = level_nr; // (currently not used)
3732 tape.random_seed = replay->seed;
3734 TapeSetDateFromIsoDateString(replay->date);
3737 tape.pos[tape.counter].delay = 0;
3739 tape.bd_replay = TRUE;
3741 // all time calculations only used to display approximate tape time
3742 int cave_speed = cave->speed;
3743 int milliseconds_game = 0;
3744 int milliseconds_elapsed = 20;
3746 for (i = 0; i < replay->movements->len; i++)
3748 int replay_action = replay->movements->data[i];
3749 int tape_action = map_action_BD_to_RND(replay_action);
3750 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3751 boolean success = 0;
3755 success = TapeAddAction(action);
3757 milliseconds_game += milliseconds_elapsed;
3759 if (milliseconds_game >= cave_speed)
3761 milliseconds_game -= cave_speed;
3768 tape.pos[tape.counter].delay = 0;
3769 tape.pos[tape.counter].action[0] = 0;
3773 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3779 TapeHaltRecording();
3783 // ----------------------------------------------------------------------------
3784 // functions for loading EM level
3785 // ----------------------------------------------------------------------------
3787 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3789 static int ball_xy[8][2] =
3800 struct LevelInfo_EM *level_em = level->native_em_level;
3801 struct CAVE *cav = level_em->cav;
3804 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3805 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3807 cav->time_seconds = level->time;
3808 cav->gems_needed = level->gems_needed;
3810 cav->emerald_score = level->score[SC_EMERALD];
3811 cav->diamond_score = level->score[SC_DIAMOND];
3812 cav->alien_score = level->score[SC_ROBOT];
3813 cav->tank_score = level->score[SC_SPACESHIP];
3814 cav->bug_score = level->score[SC_BUG];
3815 cav->eater_score = level->score[SC_YAMYAM];
3816 cav->nut_score = level->score[SC_NUT];
3817 cav->dynamite_score = level->score[SC_DYNAMITE];
3818 cav->key_score = level->score[SC_KEY];
3819 cav->exit_score = level->score[SC_TIME_BONUS];
3821 cav->num_eater_arrays = level->num_yamyam_contents;
3823 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3824 for (y = 0; y < 3; y++)
3825 for (x = 0; x < 3; x++)
3826 cav->eater_array[i][y * 3 + x] =
3827 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3829 cav->amoeba_time = level->amoeba_speed;
3830 cav->wonderwall_time = level->time_magic_wall;
3831 cav->wheel_time = level->time_wheel;
3833 cav->android_move_time = level->android_move_time;
3834 cav->android_clone_time = level->android_clone_time;
3835 cav->ball_random = level->ball_random;
3836 cav->ball_active = level->ball_active_initial;
3837 cav->ball_time = level->ball_time;
3838 cav->num_ball_arrays = level->num_ball_contents;
3840 cav->lenses_score = level->lenses_score;
3841 cav->magnify_score = level->magnify_score;
3842 cav->slurp_score = level->slurp_score;
3844 cav->lenses_time = level->lenses_time;
3845 cav->magnify_time = level->magnify_time;
3847 cav->wind_time = 9999;
3848 cav->wind_direction =
3849 map_direction_RND_to_EM(level->wind_direction_initial);
3851 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3852 for (j = 0; j < 8; j++)
3853 cav->ball_array[i][j] =
3854 map_element_RND_to_EM_cave(level->ball_content[i].
3855 e[ball_xy[j][0]][ball_xy[j][1]]);
3857 map_android_clone_elements_RND_to_EM(level);
3859 // first fill the complete playfield with the empty space element
3860 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3861 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3862 cav->cave[x][y] = Cblank;
3864 // then copy the real level contents from level file into the playfield
3865 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3867 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3869 if (level->field[x][y] == EL_AMOEBA_DEAD)
3870 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3872 cav->cave[x][y] = new_element;
3875 for (i = 0; i < MAX_PLAYERS; i++)
3877 cav->player_x[i] = -1;
3878 cav->player_y[i] = -1;
3881 // initialize player positions and delete players from the playfield
3882 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3884 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3886 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3888 cav->player_x[player_nr] = x;
3889 cav->player_y[player_nr] = y;
3891 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3896 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3898 static int ball_xy[8][2] =
3909 struct LevelInfo_EM *level_em = level->native_em_level;
3910 struct CAVE *cav = level_em->cav;
3913 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3914 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3916 level->time = cav->time_seconds;
3917 level->gems_needed = cav->gems_needed;
3919 sprintf(level->name, "Level %d", level->file_info.nr);
3921 level->score[SC_EMERALD] = cav->emerald_score;
3922 level->score[SC_DIAMOND] = cav->diamond_score;
3923 level->score[SC_ROBOT] = cav->alien_score;
3924 level->score[SC_SPACESHIP] = cav->tank_score;
3925 level->score[SC_BUG] = cav->bug_score;
3926 level->score[SC_YAMYAM] = cav->eater_score;
3927 level->score[SC_NUT] = cav->nut_score;
3928 level->score[SC_DYNAMITE] = cav->dynamite_score;
3929 level->score[SC_KEY] = cav->key_score;
3930 level->score[SC_TIME_BONUS] = cav->exit_score;
3932 level->num_yamyam_contents = cav->num_eater_arrays;
3934 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3935 for (y = 0; y < 3; y++)
3936 for (x = 0; x < 3; x++)
3937 level->yamyam_content[i].e[x][y] =
3938 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3940 level->amoeba_speed = cav->amoeba_time;
3941 level->time_magic_wall = cav->wonderwall_time;
3942 level->time_wheel = cav->wheel_time;
3944 level->android_move_time = cav->android_move_time;
3945 level->android_clone_time = cav->android_clone_time;
3946 level->ball_random = cav->ball_random;
3947 level->ball_active_initial = cav->ball_active;
3948 level->ball_time = cav->ball_time;
3949 level->num_ball_contents = cav->num_ball_arrays;
3951 level->lenses_score = cav->lenses_score;
3952 level->magnify_score = cav->magnify_score;
3953 level->slurp_score = cav->slurp_score;
3955 level->lenses_time = cav->lenses_time;
3956 level->magnify_time = cav->magnify_time;
3958 level->wind_direction_initial =
3959 map_direction_EM_to_RND(cav->wind_direction);
3961 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3962 for (j = 0; j < 8; j++)
3963 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3964 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3966 map_android_clone_elements_EM_to_RND(level);
3968 // convert the playfield (some elements need special treatment)
3969 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3971 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3973 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3974 new_element = EL_AMOEBA_DEAD;
3976 level->field[x][y] = new_element;
3979 for (i = 0; i < MAX_PLAYERS; i++)
3981 // in case of all players set to the same field, use the first player
3982 int nr = MAX_PLAYERS - i - 1;
3983 int jx = cav->player_x[nr];
3984 int jy = cav->player_y[nr];
3986 if (jx != -1 && jy != -1)
3987 level->field[jx][jy] = EL_PLAYER_1 + nr;
3990 // time score is counted for each 10 seconds left in Emerald Mine levels
3991 level->time_score_base = 10;
3995 // ----------------------------------------------------------------------------
3996 // functions for loading SP level
3997 // ----------------------------------------------------------------------------
3999 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4001 struct LevelInfo_SP *level_sp = level->native_sp_level;
4002 LevelInfoType *header = &level_sp->header;
4005 level_sp->width = level->fieldx;
4006 level_sp->height = level->fieldy;
4008 for (x = 0; x < level->fieldx; x++)
4009 for (y = 0; y < level->fieldy; y++)
4010 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4012 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4014 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4015 header->LevelTitle[i] = level->name[i];
4016 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4018 header->InfotronsNeeded = level->gems_needed;
4020 header->SpecialPortCount = 0;
4022 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4024 boolean gravity_port_found = FALSE;
4025 boolean gravity_port_valid = FALSE;
4026 int gravity_port_flag;
4027 int gravity_port_base_element;
4028 int element = level->field[x][y];
4030 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4031 element <= EL_SP_GRAVITY_ON_PORT_UP)
4033 gravity_port_found = TRUE;
4034 gravity_port_valid = TRUE;
4035 gravity_port_flag = 1;
4036 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4038 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4039 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4041 gravity_port_found = TRUE;
4042 gravity_port_valid = TRUE;
4043 gravity_port_flag = 0;
4044 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4046 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4047 element <= EL_SP_GRAVITY_PORT_UP)
4049 // change R'n'D style gravity inverting special port to normal port
4050 // (there are no gravity inverting ports in native Supaplex engine)
4052 gravity_port_found = TRUE;
4053 gravity_port_valid = FALSE;
4054 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4057 if (gravity_port_found)
4059 if (gravity_port_valid &&
4060 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4062 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4064 port->PortLocation = (y * level->fieldx + x) * 2;
4065 port->Gravity = gravity_port_flag;
4067 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4069 header->SpecialPortCount++;
4073 // change special gravity port to normal port
4075 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4078 level_sp->playfield[x][y] = element - EL_SP_START;
4083 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4085 struct LevelInfo_SP *level_sp = level->native_sp_level;
4086 LevelInfoType *header = &level_sp->header;
4087 boolean num_invalid_elements = 0;
4090 level->fieldx = level_sp->width;
4091 level->fieldy = level_sp->height;
4093 for (x = 0; x < level->fieldx; x++)
4095 for (y = 0; y < level->fieldy; y++)
4097 int element_old = level_sp->playfield[x][y];
4098 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4100 if (element_new == EL_UNKNOWN)
4102 num_invalid_elements++;
4104 Debug("level:native:SP", "invalid element %d at position %d, %d",
4108 level->field[x][y] = element_new;
4112 if (num_invalid_elements > 0)
4113 Warn("found %d invalid elements%s", num_invalid_elements,
4114 (!options.debug ? " (use '--debug' for more details)" : ""));
4116 for (i = 0; i < MAX_PLAYERS; i++)
4117 level->initial_player_gravity[i] =
4118 (header->InitialGravity == 1 ? TRUE : FALSE);
4120 // skip leading spaces
4121 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4122 if (header->LevelTitle[i] != ' ')
4126 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4127 level->name[j] = header->LevelTitle[i];
4128 level->name[j] = '\0';
4130 // cut trailing spaces
4132 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4133 level->name[j - 1] = '\0';
4135 level->gems_needed = header->InfotronsNeeded;
4137 for (i = 0; i < header->SpecialPortCount; i++)
4139 SpecialPortType *port = &header->SpecialPort[i];
4140 int port_location = port->PortLocation;
4141 int gravity = port->Gravity;
4142 int port_x, port_y, port_element;
4144 port_x = (port_location / 2) % level->fieldx;
4145 port_y = (port_location / 2) / level->fieldx;
4147 if (port_x < 0 || port_x >= level->fieldx ||
4148 port_y < 0 || port_y >= level->fieldy)
4150 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4155 port_element = level->field[port_x][port_y];
4157 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4158 port_element > EL_SP_GRAVITY_PORT_UP)
4160 Warn("no special port at position (%d, %d)", port_x, port_y);
4165 // change previous (wrong) gravity inverting special port to either
4166 // gravity enabling special port or gravity disabling special port
4167 level->field[port_x][port_y] +=
4168 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4169 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4172 // change special gravity ports without database entries to normal ports
4173 for (x = 0; x < level->fieldx; x++)
4174 for (y = 0; y < level->fieldy; y++)
4175 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4176 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4177 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4179 level->time = 0; // no time limit
4180 level->amoeba_speed = 0;
4181 level->time_magic_wall = 0;
4182 level->time_wheel = 0;
4183 level->amoeba_content = EL_EMPTY;
4185 // original Supaplex does not use score values -- rate by playing time
4186 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4187 level->score[i] = 0;
4189 level->rate_time_over_score = TRUE;
4191 // there are no yamyams in supaplex levels
4192 for (i = 0; i < level->num_yamyam_contents; i++)
4193 for (x = 0; x < 3; x++)
4194 for (y = 0; y < 3; y++)
4195 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4198 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4200 struct LevelInfo_SP *level_sp = level->native_sp_level;
4201 struct DemoInfo_SP *demo = &level_sp->demo;
4204 // always start with reliable default values
4205 demo->is_available = FALSE;
4208 if (TAPE_IS_EMPTY(tape))
4211 demo->level_nr = tape.level_nr; // (currently not used)
4213 level_sp->header.DemoRandomSeed = tape.random_seed;
4217 for (i = 0; i < tape.length; i++)
4219 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4220 int demo_repeat = tape.pos[i].delay;
4221 int demo_entries = (demo_repeat + 15) / 16;
4223 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4225 Warn("tape truncated: size exceeds maximum SP demo size %d",
4231 for (j = 0; j < demo_repeat / 16; j++)
4232 demo->data[demo->length++] = 0xf0 | demo_action;
4234 if (demo_repeat % 16)
4235 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4238 demo->is_available = TRUE;
4241 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4243 struct LevelInfo_SP *level_sp = level->native_sp_level;
4244 struct DemoInfo_SP *demo = &level_sp->demo;
4245 char *filename = level->file_info.filename;
4248 // always start with reliable default values
4249 setTapeInfoToDefaults();
4251 if (!demo->is_available)
4254 tape.level_nr = demo->level_nr; // (currently not used)
4255 tape.random_seed = level_sp->header.DemoRandomSeed;
4257 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4260 tape.pos[tape.counter].delay = 0;
4262 for (i = 0; i < demo->length; i++)
4264 int demo_action = demo->data[i] & 0x0f;
4265 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4266 int tape_action = map_key_SP_to_RND(demo_action);
4267 int tape_repeat = demo_repeat + 1;
4268 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4269 boolean success = 0;
4272 for (j = 0; j < tape_repeat; j++)
4273 success = TapeAddAction(action);
4277 Warn("SP demo truncated: size exceeds maximum tape size %d",
4284 TapeHaltRecording();
4288 // ----------------------------------------------------------------------------
4289 // functions for loading MM level
4290 // ----------------------------------------------------------------------------
4292 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4294 struct LevelInfo_MM *level_mm = level->native_mm_level;
4297 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4298 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4300 level_mm->time = level->time;
4301 level_mm->kettles_needed = level->gems_needed;
4302 level_mm->auto_count_kettles = level->auto_count_gems;
4304 level_mm->mm_laser_red = level->mm_laser_red;
4305 level_mm->mm_laser_green = level->mm_laser_green;
4306 level_mm->mm_laser_blue = level->mm_laser_blue;
4308 level_mm->df_laser_red = level->df_laser_red;
4309 level_mm->df_laser_green = level->df_laser_green;
4310 level_mm->df_laser_blue = level->df_laser_blue;
4312 strcpy(level_mm->name, level->name);
4313 strcpy(level_mm->author, level->author);
4315 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4316 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4317 level_mm->score[SC_KEY] = level->score[SC_KEY];
4318 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4319 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4321 level_mm->amoeba_speed = level->amoeba_speed;
4322 level_mm->time_fuse = level->mm_time_fuse;
4323 level_mm->time_bomb = level->mm_time_bomb;
4324 level_mm->time_ball = level->mm_time_ball;
4325 level_mm->time_block = level->mm_time_block;
4327 level_mm->num_ball_contents = level->num_mm_ball_contents;
4328 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4329 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4330 level_mm->explode_ball = level->explode_mm_ball;
4332 for (i = 0; i < level->num_mm_ball_contents; i++)
4333 level_mm->ball_content[i] =
4334 map_element_RND_to_MM(level->mm_ball_content[i]);
4336 for (x = 0; x < level->fieldx; x++)
4337 for (y = 0; y < level->fieldy; y++)
4339 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4342 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4344 struct LevelInfo_MM *level_mm = level->native_mm_level;
4347 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4348 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4350 level->time = level_mm->time;
4351 level->gems_needed = level_mm->kettles_needed;
4352 level->auto_count_gems = level_mm->auto_count_kettles;
4354 level->mm_laser_red = level_mm->mm_laser_red;
4355 level->mm_laser_green = level_mm->mm_laser_green;
4356 level->mm_laser_blue = level_mm->mm_laser_blue;
4358 level->df_laser_red = level_mm->df_laser_red;
4359 level->df_laser_green = level_mm->df_laser_green;
4360 level->df_laser_blue = level_mm->df_laser_blue;
4362 strcpy(level->name, level_mm->name);
4364 // only overwrite author from 'levelinfo.conf' if author defined in level
4365 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4366 strcpy(level->author, level_mm->author);
4368 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4369 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4370 level->score[SC_KEY] = level_mm->score[SC_KEY];
4371 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4372 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4374 level->amoeba_speed = level_mm->amoeba_speed;
4375 level->mm_time_fuse = level_mm->time_fuse;
4376 level->mm_time_bomb = level_mm->time_bomb;
4377 level->mm_time_ball = level_mm->time_ball;
4378 level->mm_time_block = level_mm->time_block;
4380 level->num_mm_ball_contents = level_mm->num_ball_contents;
4381 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4382 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4383 level->explode_mm_ball = level_mm->explode_ball;
4385 for (i = 0; i < level->num_mm_ball_contents; i++)
4386 level->mm_ball_content[i] =
4387 map_element_MM_to_RND(level_mm->ball_content[i]);
4389 for (x = 0; x < level->fieldx; x++)
4390 for (y = 0; y < level->fieldy; y++)
4391 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4395 // ----------------------------------------------------------------------------
4396 // functions for loading DC level
4397 // ----------------------------------------------------------------------------
4399 #define DC_LEVEL_HEADER_SIZE 344
4401 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4404 static int last_data_encoded;
4408 int diff_hi, diff_lo;
4409 int data_hi, data_lo;
4410 unsigned short data_decoded;
4414 last_data_encoded = 0;
4421 diff = data_encoded - last_data_encoded;
4422 diff_hi = diff & ~0xff;
4423 diff_lo = diff & 0xff;
4427 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4428 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4429 data_hi = data_hi & 0xff00;
4431 data_decoded = data_hi | data_lo;
4433 last_data_encoded = data_encoded;
4435 offset1 = (offset1 + 1) % 31;
4436 offset2 = offset2 & 0xff;
4438 return data_decoded;
4441 static int getMappedElement_DC(int element)
4449 // 0x0117 - 0x036e: (?)
4452 // 0x042d - 0x0684: (?)
4468 element = EL_CRYSTAL;
4471 case 0x0e77: // quicksand (boulder)
4472 element = EL_QUICKSAND_FAST_FULL;
4475 case 0x0e99: // slow quicksand (boulder)
4476 element = EL_QUICKSAND_FULL;
4480 element = EL_EM_EXIT_OPEN;
4484 element = EL_EM_EXIT_CLOSED;
4488 element = EL_EM_STEEL_EXIT_OPEN;
4492 element = EL_EM_STEEL_EXIT_CLOSED;
4495 case 0x0f4f: // dynamite (lit 1)
4496 element = EL_EM_DYNAMITE_ACTIVE;
4499 case 0x0f57: // dynamite (lit 2)
4500 element = EL_EM_DYNAMITE_ACTIVE;
4503 case 0x0f5f: // dynamite (lit 3)
4504 element = EL_EM_DYNAMITE_ACTIVE;
4507 case 0x0f67: // dynamite (lit 4)
4508 element = EL_EM_DYNAMITE_ACTIVE;
4515 element = EL_AMOEBA_WET;
4519 element = EL_AMOEBA_DROP;
4523 element = EL_DC_MAGIC_WALL;
4527 element = EL_SPACESHIP_UP;
4531 element = EL_SPACESHIP_DOWN;
4535 element = EL_SPACESHIP_LEFT;
4539 element = EL_SPACESHIP_RIGHT;
4543 element = EL_BUG_UP;
4547 element = EL_BUG_DOWN;
4551 element = EL_BUG_LEFT;
4555 element = EL_BUG_RIGHT;
4559 element = EL_MOLE_UP;
4563 element = EL_MOLE_DOWN;
4567 element = EL_MOLE_LEFT;
4571 element = EL_MOLE_RIGHT;
4579 element = EL_YAMYAM_UP;
4583 element = EL_SWITCHGATE_OPEN;
4587 element = EL_SWITCHGATE_CLOSED;
4591 element = EL_DC_SWITCHGATE_SWITCH_UP;
4595 element = EL_TIMEGATE_CLOSED;
4598 case 0x144c: // conveyor belt switch (green)
4599 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4602 case 0x144f: // conveyor belt switch (red)
4603 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4606 case 0x1452: // conveyor belt switch (blue)
4607 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4611 element = EL_CONVEYOR_BELT_3_MIDDLE;
4615 element = EL_CONVEYOR_BELT_3_LEFT;
4619 element = EL_CONVEYOR_BELT_3_RIGHT;
4623 element = EL_CONVEYOR_BELT_1_MIDDLE;
4627 element = EL_CONVEYOR_BELT_1_LEFT;
4631 element = EL_CONVEYOR_BELT_1_RIGHT;
4635 element = EL_CONVEYOR_BELT_4_MIDDLE;
4639 element = EL_CONVEYOR_BELT_4_LEFT;
4643 element = EL_CONVEYOR_BELT_4_RIGHT;
4647 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4651 element = EL_EXPANDABLE_WALL_VERTICAL;
4655 element = EL_EXPANDABLE_WALL_ANY;
4658 case 0x14ce: // growing steel wall (left/right)
4659 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4662 case 0x14df: // growing steel wall (up/down)
4663 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4666 case 0x14e8: // growing steel wall (up/down/left/right)
4667 element = EL_EXPANDABLE_STEELWALL_ANY;
4671 element = EL_SHIELD_DEADLY;
4675 element = EL_EXTRA_TIME;
4683 element = EL_EMPTY_SPACE;
4686 case 0x1578: // quicksand (empty)
4687 element = EL_QUICKSAND_FAST_EMPTY;
4690 case 0x1579: // slow quicksand (empty)
4691 element = EL_QUICKSAND_EMPTY;
4701 element = EL_EM_DYNAMITE;
4704 case 0x15a1: // key (red)
4705 element = EL_EM_KEY_1;
4708 case 0x15a2: // key (yellow)
4709 element = EL_EM_KEY_2;
4712 case 0x15a3: // key (blue)
4713 element = EL_EM_KEY_4;
4716 case 0x15a4: // key (green)
4717 element = EL_EM_KEY_3;
4720 case 0x15a5: // key (white)
4721 element = EL_DC_KEY_WHITE;
4725 element = EL_WALL_SLIPPERY;
4732 case 0x15a8: // wall (not round)
4736 case 0x15a9: // (blue)
4737 element = EL_CHAR_A;
4740 case 0x15aa: // (blue)
4741 element = EL_CHAR_B;
4744 case 0x15ab: // (blue)
4745 element = EL_CHAR_C;
4748 case 0x15ac: // (blue)
4749 element = EL_CHAR_D;
4752 case 0x15ad: // (blue)
4753 element = EL_CHAR_E;
4756 case 0x15ae: // (blue)
4757 element = EL_CHAR_F;
4760 case 0x15af: // (blue)
4761 element = EL_CHAR_G;
4764 case 0x15b0: // (blue)
4765 element = EL_CHAR_H;
4768 case 0x15b1: // (blue)
4769 element = EL_CHAR_I;
4772 case 0x15b2: // (blue)
4773 element = EL_CHAR_J;
4776 case 0x15b3: // (blue)
4777 element = EL_CHAR_K;
4780 case 0x15b4: // (blue)
4781 element = EL_CHAR_L;
4784 case 0x15b5: // (blue)
4785 element = EL_CHAR_M;
4788 case 0x15b6: // (blue)
4789 element = EL_CHAR_N;
4792 case 0x15b7: // (blue)
4793 element = EL_CHAR_O;
4796 case 0x15b8: // (blue)
4797 element = EL_CHAR_P;
4800 case 0x15b9: // (blue)
4801 element = EL_CHAR_Q;
4804 case 0x15ba: // (blue)
4805 element = EL_CHAR_R;
4808 case 0x15bb: // (blue)
4809 element = EL_CHAR_S;
4812 case 0x15bc: // (blue)
4813 element = EL_CHAR_T;
4816 case 0x15bd: // (blue)
4817 element = EL_CHAR_U;
4820 case 0x15be: // (blue)
4821 element = EL_CHAR_V;
4824 case 0x15bf: // (blue)
4825 element = EL_CHAR_W;
4828 case 0x15c0: // (blue)
4829 element = EL_CHAR_X;
4832 case 0x15c1: // (blue)
4833 element = EL_CHAR_Y;
4836 case 0x15c2: // (blue)
4837 element = EL_CHAR_Z;
4840 case 0x15c3: // (blue)
4841 element = EL_CHAR_AUMLAUT;
4844 case 0x15c4: // (blue)
4845 element = EL_CHAR_OUMLAUT;
4848 case 0x15c5: // (blue)
4849 element = EL_CHAR_UUMLAUT;
4852 case 0x15c6: // (blue)
4853 element = EL_CHAR_0;
4856 case 0x15c7: // (blue)
4857 element = EL_CHAR_1;
4860 case 0x15c8: // (blue)
4861 element = EL_CHAR_2;
4864 case 0x15c9: // (blue)
4865 element = EL_CHAR_3;
4868 case 0x15ca: // (blue)
4869 element = EL_CHAR_4;
4872 case 0x15cb: // (blue)
4873 element = EL_CHAR_5;
4876 case 0x15cc: // (blue)
4877 element = EL_CHAR_6;
4880 case 0x15cd: // (blue)
4881 element = EL_CHAR_7;
4884 case 0x15ce: // (blue)
4885 element = EL_CHAR_8;
4888 case 0x15cf: // (blue)
4889 element = EL_CHAR_9;
4892 case 0x15d0: // (blue)
4893 element = EL_CHAR_PERIOD;
4896 case 0x15d1: // (blue)
4897 element = EL_CHAR_EXCLAM;
4900 case 0x15d2: // (blue)
4901 element = EL_CHAR_COLON;
4904 case 0x15d3: // (blue)
4905 element = EL_CHAR_LESS;
4908 case 0x15d4: // (blue)
4909 element = EL_CHAR_GREATER;
4912 case 0x15d5: // (blue)
4913 element = EL_CHAR_QUESTION;
4916 case 0x15d6: // (blue)
4917 element = EL_CHAR_COPYRIGHT;
4920 case 0x15d7: // (blue)
4921 element = EL_CHAR_UP;
4924 case 0x15d8: // (blue)
4925 element = EL_CHAR_DOWN;
4928 case 0x15d9: // (blue)
4929 element = EL_CHAR_BUTTON;
4932 case 0x15da: // (blue)
4933 element = EL_CHAR_PLUS;
4936 case 0x15db: // (blue)
4937 element = EL_CHAR_MINUS;
4940 case 0x15dc: // (blue)
4941 element = EL_CHAR_APOSTROPHE;
4944 case 0x15dd: // (blue)
4945 element = EL_CHAR_PARENLEFT;
4948 case 0x15de: // (blue)
4949 element = EL_CHAR_PARENRIGHT;
4952 case 0x15df: // (green)
4953 element = EL_CHAR_A;
4956 case 0x15e0: // (green)
4957 element = EL_CHAR_B;
4960 case 0x15e1: // (green)
4961 element = EL_CHAR_C;
4964 case 0x15e2: // (green)
4965 element = EL_CHAR_D;
4968 case 0x15e3: // (green)
4969 element = EL_CHAR_E;
4972 case 0x15e4: // (green)
4973 element = EL_CHAR_F;
4976 case 0x15e5: // (green)
4977 element = EL_CHAR_G;
4980 case 0x15e6: // (green)
4981 element = EL_CHAR_H;
4984 case 0x15e7: // (green)
4985 element = EL_CHAR_I;
4988 case 0x15e8: // (green)
4989 element = EL_CHAR_J;
4992 case 0x15e9: // (green)
4993 element = EL_CHAR_K;
4996 case 0x15ea: // (green)
4997 element = EL_CHAR_L;
5000 case 0x15eb: // (green)
5001 element = EL_CHAR_M;
5004 case 0x15ec: // (green)
5005 element = EL_CHAR_N;
5008 case 0x15ed: // (green)
5009 element = EL_CHAR_O;
5012 case 0x15ee: // (green)
5013 element = EL_CHAR_P;
5016 case 0x15ef: // (green)
5017 element = EL_CHAR_Q;
5020 case 0x15f0: // (green)
5021 element = EL_CHAR_R;
5024 case 0x15f1: // (green)
5025 element = EL_CHAR_S;
5028 case 0x15f2: // (green)
5029 element = EL_CHAR_T;
5032 case 0x15f3: // (green)
5033 element = EL_CHAR_U;
5036 case 0x15f4: // (green)
5037 element = EL_CHAR_V;
5040 case 0x15f5: // (green)
5041 element = EL_CHAR_W;
5044 case 0x15f6: // (green)
5045 element = EL_CHAR_X;
5048 case 0x15f7: // (green)
5049 element = EL_CHAR_Y;
5052 case 0x15f8: // (green)
5053 element = EL_CHAR_Z;
5056 case 0x15f9: // (green)
5057 element = EL_CHAR_AUMLAUT;
5060 case 0x15fa: // (green)
5061 element = EL_CHAR_OUMLAUT;
5064 case 0x15fb: // (green)
5065 element = EL_CHAR_UUMLAUT;
5068 case 0x15fc: // (green)
5069 element = EL_CHAR_0;
5072 case 0x15fd: // (green)
5073 element = EL_CHAR_1;
5076 case 0x15fe: // (green)
5077 element = EL_CHAR_2;
5080 case 0x15ff: // (green)
5081 element = EL_CHAR_3;
5084 case 0x1600: // (green)
5085 element = EL_CHAR_4;
5088 case 0x1601: // (green)
5089 element = EL_CHAR_5;
5092 case 0x1602: // (green)
5093 element = EL_CHAR_6;
5096 case 0x1603: // (green)
5097 element = EL_CHAR_7;
5100 case 0x1604: // (green)
5101 element = EL_CHAR_8;
5104 case 0x1605: // (green)
5105 element = EL_CHAR_9;
5108 case 0x1606: // (green)
5109 element = EL_CHAR_PERIOD;
5112 case 0x1607: // (green)
5113 element = EL_CHAR_EXCLAM;
5116 case 0x1608: // (green)
5117 element = EL_CHAR_COLON;
5120 case 0x1609: // (green)
5121 element = EL_CHAR_LESS;
5124 case 0x160a: // (green)
5125 element = EL_CHAR_GREATER;
5128 case 0x160b: // (green)
5129 element = EL_CHAR_QUESTION;
5132 case 0x160c: // (green)
5133 element = EL_CHAR_COPYRIGHT;
5136 case 0x160d: // (green)
5137 element = EL_CHAR_UP;
5140 case 0x160e: // (green)
5141 element = EL_CHAR_DOWN;
5144 case 0x160f: // (green)
5145 element = EL_CHAR_BUTTON;
5148 case 0x1610: // (green)
5149 element = EL_CHAR_PLUS;
5152 case 0x1611: // (green)
5153 element = EL_CHAR_MINUS;
5156 case 0x1612: // (green)
5157 element = EL_CHAR_APOSTROPHE;
5160 case 0x1613: // (green)
5161 element = EL_CHAR_PARENLEFT;
5164 case 0x1614: // (green)
5165 element = EL_CHAR_PARENRIGHT;
5168 case 0x1615: // (blue steel)
5169 element = EL_STEEL_CHAR_A;
5172 case 0x1616: // (blue steel)
5173 element = EL_STEEL_CHAR_B;
5176 case 0x1617: // (blue steel)
5177 element = EL_STEEL_CHAR_C;
5180 case 0x1618: // (blue steel)
5181 element = EL_STEEL_CHAR_D;
5184 case 0x1619: // (blue steel)
5185 element = EL_STEEL_CHAR_E;
5188 case 0x161a: // (blue steel)
5189 element = EL_STEEL_CHAR_F;
5192 case 0x161b: // (blue steel)
5193 element = EL_STEEL_CHAR_G;
5196 case 0x161c: // (blue steel)
5197 element = EL_STEEL_CHAR_H;
5200 case 0x161d: // (blue steel)
5201 element = EL_STEEL_CHAR_I;
5204 case 0x161e: // (blue steel)
5205 element = EL_STEEL_CHAR_J;
5208 case 0x161f: // (blue steel)
5209 element = EL_STEEL_CHAR_K;
5212 case 0x1620: // (blue steel)
5213 element = EL_STEEL_CHAR_L;
5216 case 0x1621: // (blue steel)
5217 element = EL_STEEL_CHAR_M;
5220 case 0x1622: // (blue steel)
5221 element = EL_STEEL_CHAR_N;
5224 case 0x1623: // (blue steel)
5225 element = EL_STEEL_CHAR_O;
5228 case 0x1624: // (blue steel)
5229 element = EL_STEEL_CHAR_P;
5232 case 0x1625: // (blue steel)
5233 element = EL_STEEL_CHAR_Q;
5236 case 0x1626: // (blue steel)
5237 element = EL_STEEL_CHAR_R;
5240 case 0x1627: // (blue steel)
5241 element = EL_STEEL_CHAR_S;
5244 case 0x1628: // (blue steel)
5245 element = EL_STEEL_CHAR_T;
5248 case 0x1629: // (blue steel)
5249 element = EL_STEEL_CHAR_U;
5252 case 0x162a: // (blue steel)
5253 element = EL_STEEL_CHAR_V;
5256 case 0x162b: // (blue steel)
5257 element = EL_STEEL_CHAR_W;
5260 case 0x162c: // (blue steel)
5261 element = EL_STEEL_CHAR_X;
5264 case 0x162d: // (blue steel)
5265 element = EL_STEEL_CHAR_Y;
5268 case 0x162e: // (blue steel)
5269 element = EL_STEEL_CHAR_Z;
5272 case 0x162f: // (blue steel)
5273 element = EL_STEEL_CHAR_AUMLAUT;
5276 case 0x1630: // (blue steel)
5277 element = EL_STEEL_CHAR_OUMLAUT;
5280 case 0x1631: // (blue steel)
5281 element = EL_STEEL_CHAR_UUMLAUT;
5284 case 0x1632: // (blue steel)
5285 element = EL_STEEL_CHAR_0;
5288 case 0x1633: // (blue steel)
5289 element = EL_STEEL_CHAR_1;
5292 case 0x1634: // (blue steel)
5293 element = EL_STEEL_CHAR_2;
5296 case 0x1635: // (blue steel)
5297 element = EL_STEEL_CHAR_3;
5300 case 0x1636: // (blue steel)
5301 element = EL_STEEL_CHAR_4;
5304 case 0x1637: // (blue steel)
5305 element = EL_STEEL_CHAR_5;
5308 case 0x1638: // (blue steel)
5309 element = EL_STEEL_CHAR_6;
5312 case 0x1639: // (blue steel)
5313 element = EL_STEEL_CHAR_7;
5316 case 0x163a: // (blue steel)
5317 element = EL_STEEL_CHAR_8;
5320 case 0x163b: // (blue steel)
5321 element = EL_STEEL_CHAR_9;
5324 case 0x163c: // (blue steel)
5325 element = EL_STEEL_CHAR_PERIOD;
5328 case 0x163d: // (blue steel)
5329 element = EL_STEEL_CHAR_EXCLAM;
5332 case 0x163e: // (blue steel)
5333 element = EL_STEEL_CHAR_COLON;
5336 case 0x163f: // (blue steel)
5337 element = EL_STEEL_CHAR_LESS;
5340 case 0x1640: // (blue steel)
5341 element = EL_STEEL_CHAR_GREATER;
5344 case 0x1641: // (blue steel)
5345 element = EL_STEEL_CHAR_QUESTION;
5348 case 0x1642: // (blue steel)
5349 element = EL_STEEL_CHAR_COPYRIGHT;
5352 case 0x1643: // (blue steel)
5353 element = EL_STEEL_CHAR_UP;
5356 case 0x1644: // (blue steel)
5357 element = EL_STEEL_CHAR_DOWN;
5360 case 0x1645: // (blue steel)
5361 element = EL_STEEL_CHAR_BUTTON;
5364 case 0x1646: // (blue steel)
5365 element = EL_STEEL_CHAR_PLUS;
5368 case 0x1647: // (blue steel)
5369 element = EL_STEEL_CHAR_MINUS;
5372 case 0x1648: // (blue steel)
5373 element = EL_STEEL_CHAR_APOSTROPHE;
5376 case 0x1649: // (blue steel)
5377 element = EL_STEEL_CHAR_PARENLEFT;
5380 case 0x164a: // (blue steel)
5381 element = EL_STEEL_CHAR_PARENRIGHT;
5384 case 0x164b: // (green steel)
5385 element = EL_STEEL_CHAR_A;
5388 case 0x164c: // (green steel)
5389 element = EL_STEEL_CHAR_B;
5392 case 0x164d: // (green steel)
5393 element = EL_STEEL_CHAR_C;
5396 case 0x164e: // (green steel)
5397 element = EL_STEEL_CHAR_D;
5400 case 0x164f: // (green steel)
5401 element = EL_STEEL_CHAR_E;
5404 case 0x1650: // (green steel)
5405 element = EL_STEEL_CHAR_F;
5408 case 0x1651: // (green steel)
5409 element = EL_STEEL_CHAR_G;
5412 case 0x1652: // (green steel)
5413 element = EL_STEEL_CHAR_H;
5416 case 0x1653: // (green steel)
5417 element = EL_STEEL_CHAR_I;
5420 case 0x1654: // (green steel)
5421 element = EL_STEEL_CHAR_J;
5424 case 0x1655: // (green steel)
5425 element = EL_STEEL_CHAR_K;
5428 case 0x1656: // (green steel)
5429 element = EL_STEEL_CHAR_L;
5432 case 0x1657: // (green steel)
5433 element = EL_STEEL_CHAR_M;
5436 case 0x1658: // (green steel)
5437 element = EL_STEEL_CHAR_N;
5440 case 0x1659: // (green steel)
5441 element = EL_STEEL_CHAR_O;
5444 case 0x165a: // (green steel)
5445 element = EL_STEEL_CHAR_P;
5448 case 0x165b: // (green steel)
5449 element = EL_STEEL_CHAR_Q;
5452 case 0x165c: // (green steel)
5453 element = EL_STEEL_CHAR_R;
5456 case 0x165d: // (green steel)
5457 element = EL_STEEL_CHAR_S;
5460 case 0x165e: // (green steel)
5461 element = EL_STEEL_CHAR_T;
5464 case 0x165f: // (green steel)
5465 element = EL_STEEL_CHAR_U;
5468 case 0x1660: // (green steel)
5469 element = EL_STEEL_CHAR_V;
5472 case 0x1661: // (green steel)
5473 element = EL_STEEL_CHAR_W;
5476 case 0x1662: // (green steel)
5477 element = EL_STEEL_CHAR_X;
5480 case 0x1663: // (green steel)
5481 element = EL_STEEL_CHAR_Y;
5484 case 0x1664: // (green steel)
5485 element = EL_STEEL_CHAR_Z;
5488 case 0x1665: // (green steel)
5489 element = EL_STEEL_CHAR_AUMLAUT;
5492 case 0x1666: // (green steel)
5493 element = EL_STEEL_CHAR_OUMLAUT;
5496 case 0x1667: // (green steel)
5497 element = EL_STEEL_CHAR_UUMLAUT;
5500 case 0x1668: // (green steel)
5501 element = EL_STEEL_CHAR_0;
5504 case 0x1669: // (green steel)
5505 element = EL_STEEL_CHAR_1;
5508 case 0x166a: // (green steel)
5509 element = EL_STEEL_CHAR_2;
5512 case 0x166b: // (green steel)
5513 element = EL_STEEL_CHAR_3;
5516 case 0x166c: // (green steel)
5517 element = EL_STEEL_CHAR_4;
5520 case 0x166d: // (green steel)
5521 element = EL_STEEL_CHAR_5;
5524 case 0x166e: // (green steel)
5525 element = EL_STEEL_CHAR_6;
5528 case 0x166f: // (green steel)
5529 element = EL_STEEL_CHAR_7;
5532 case 0x1670: // (green steel)
5533 element = EL_STEEL_CHAR_8;
5536 case 0x1671: // (green steel)
5537 element = EL_STEEL_CHAR_9;
5540 case 0x1672: // (green steel)
5541 element = EL_STEEL_CHAR_PERIOD;
5544 case 0x1673: // (green steel)
5545 element = EL_STEEL_CHAR_EXCLAM;
5548 case 0x1674: // (green steel)
5549 element = EL_STEEL_CHAR_COLON;
5552 case 0x1675: // (green steel)
5553 element = EL_STEEL_CHAR_LESS;
5556 case 0x1676: // (green steel)
5557 element = EL_STEEL_CHAR_GREATER;
5560 case 0x1677: // (green steel)
5561 element = EL_STEEL_CHAR_QUESTION;
5564 case 0x1678: // (green steel)
5565 element = EL_STEEL_CHAR_COPYRIGHT;
5568 case 0x1679: // (green steel)
5569 element = EL_STEEL_CHAR_UP;
5572 case 0x167a: // (green steel)
5573 element = EL_STEEL_CHAR_DOWN;
5576 case 0x167b: // (green steel)
5577 element = EL_STEEL_CHAR_BUTTON;
5580 case 0x167c: // (green steel)
5581 element = EL_STEEL_CHAR_PLUS;
5584 case 0x167d: // (green steel)
5585 element = EL_STEEL_CHAR_MINUS;
5588 case 0x167e: // (green steel)
5589 element = EL_STEEL_CHAR_APOSTROPHE;
5592 case 0x167f: // (green steel)
5593 element = EL_STEEL_CHAR_PARENLEFT;
5596 case 0x1680: // (green steel)
5597 element = EL_STEEL_CHAR_PARENRIGHT;
5600 case 0x1681: // gate (red)
5601 element = EL_EM_GATE_1;
5604 case 0x1682: // secret gate (red)
5605 element = EL_EM_GATE_1_GRAY;
5608 case 0x1683: // gate (yellow)
5609 element = EL_EM_GATE_2;
5612 case 0x1684: // secret gate (yellow)
5613 element = EL_EM_GATE_2_GRAY;
5616 case 0x1685: // gate (blue)
5617 element = EL_EM_GATE_4;
5620 case 0x1686: // secret gate (blue)
5621 element = EL_EM_GATE_4_GRAY;
5624 case 0x1687: // gate (green)
5625 element = EL_EM_GATE_3;
5628 case 0x1688: // secret gate (green)
5629 element = EL_EM_GATE_3_GRAY;
5632 case 0x1689: // gate (white)
5633 element = EL_DC_GATE_WHITE;
5636 case 0x168a: // secret gate (white)
5637 element = EL_DC_GATE_WHITE_GRAY;
5640 case 0x168b: // secret gate (no key)
5641 element = EL_DC_GATE_FAKE_GRAY;
5645 element = EL_ROBOT_WHEEL;
5649 element = EL_DC_TIMEGATE_SWITCH;
5653 element = EL_ACID_POOL_BOTTOM;
5657 element = EL_ACID_POOL_TOPLEFT;
5661 element = EL_ACID_POOL_TOPRIGHT;
5665 element = EL_ACID_POOL_BOTTOMLEFT;
5669 element = EL_ACID_POOL_BOTTOMRIGHT;
5673 element = EL_STEELWALL;
5677 element = EL_STEELWALL_SLIPPERY;
5680 case 0x1695: // steel wall (not round)
5681 element = EL_STEELWALL;
5684 case 0x1696: // steel wall (left)
5685 element = EL_DC_STEELWALL_1_LEFT;
5688 case 0x1697: // steel wall (bottom)
5689 element = EL_DC_STEELWALL_1_BOTTOM;
5692 case 0x1698: // steel wall (right)
5693 element = EL_DC_STEELWALL_1_RIGHT;
5696 case 0x1699: // steel wall (top)
5697 element = EL_DC_STEELWALL_1_TOP;
5700 case 0x169a: // steel wall (left/bottom)
5701 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5704 case 0x169b: // steel wall (right/bottom)
5705 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5708 case 0x169c: // steel wall (right/top)
5709 element = EL_DC_STEELWALL_1_TOPRIGHT;
5712 case 0x169d: // steel wall (left/top)
5713 element = EL_DC_STEELWALL_1_TOPLEFT;
5716 case 0x169e: // steel wall (right/bottom small)
5717 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5720 case 0x169f: // steel wall (left/bottom small)
5721 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5724 case 0x16a0: // steel wall (right/top small)
5725 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5728 case 0x16a1: // steel wall (left/top small)
5729 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5732 case 0x16a2: // steel wall (left/right)
5733 element = EL_DC_STEELWALL_1_VERTICAL;
5736 case 0x16a3: // steel wall (top/bottom)
5737 element = EL_DC_STEELWALL_1_HORIZONTAL;
5740 case 0x16a4: // steel wall 2 (left end)
5741 element = EL_DC_STEELWALL_2_LEFT;
5744 case 0x16a5: // steel wall 2 (right end)
5745 element = EL_DC_STEELWALL_2_RIGHT;
5748 case 0x16a6: // steel wall 2 (top end)
5749 element = EL_DC_STEELWALL_2_TOP;
5752 case 0x16a7: // steel wall 2 (bottom end)
5753 element = EL_DC_STEELWALL_2_BOTTOM;
5756 case 0x16a8: // steel wall 2 (left/right)
5757 element = EL_DC_STEELWALL_2_HORIZONTAL;
5760 case 0x16a9: // steel wall 2 (up/down)
5761 element = EL_DC_STEELWALL_2_VERTICAL;
5764 case 0x16aa: // steel wall 2 (mid)
5765 element = EL_DC_STEELWALL_2_MIDDLE;
5769 element = EL_SIGN_EXCLAMATION;
5773 element = EL_SIGN_RADIOACTIVITY;
5777 element = EL_SIGN_STOP;
5781 element = EL_SIGN_WHEELCHAIR;
5785 element = EL_SIGN_PARKING;
5789 element = EL_SIGN_NO_ENTRY;
5793 element = EL_SIGN_HEART;
5797 element = EL_SIGN_GIVE_WAY;
5801 element = EL_SIGN_ENTRY_FORBIDDEN;
5805 element = EL_SIGN_EMERGENCY_EXIT;
5809 element = EL_SIGN_YIN_YANG;
5813 element = EL_WALL_EMERALD;
5817 element = EL_WALL_DIAMOND;
5821 element = EL_WALL_PEARL;
5825 element = EL_WALL_CRYSTAL;
5829 element = EL_INVISIBLE_WALL;
5833 element = EL_INVISIBLE_STEELWALL;
5837 // EL_INVISIBLE_SAND
5840 element = EL_LIGHT_SWITCH;
5844 element = EL_ENVELOPE_1;
5848 if (element >= 0x0117 && element <= 0x036e) // (?)
5849 element = EL_DIAMOND;
5850 else if (element >= 0x042d && element <= 0x0684) // (?)
5851 element = EL_EMERALD;
5852 else if (element >= 0x157c && element <= 0x158b)
5854 else if (element >= 0x1590 && element <= 0x159f)
5855 element = EL_DC_LANDMINE;
5856 else if (element >= 0x16bc && element <= 0x16cb)
5857 element = EL_INVISIBLE_SAND;
5860 Warn("unknown Diamond Caves element 0x%04x", element);
5862 element = EL_UNKNOWN;
5867 return getMappedElement(element);
5870 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5872 byte header[DC_LEVEL_HEADER_SIZE];
5874 int envelope_header_pos = 62;
5875 int envelope_content_pos = 94;
5876 int level_name_pos = 251;
5877 int level_author_pos = 292;
5878 int envelope_header_len;
5879 int envelope_content_len;
5881 int level_author_len;
5883 int num_yamyam_contents;
5886 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5888 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5890 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5892 header[i * 2 + 0] = header_word >> 8;
5893 header[i * 2 + 1] = header_word & 0xff;
5896 // read some values from level header to check level decoding integrity
5897 fieldx = header[6] | (header[7] << 8);
5898 fieldy = header[8] | (header[9] << 8);
5899 num_yamyam_contents = header[60] | (header[61] << 8);
5901 // do some simple sanity checks to ensure that level was correctly decoded
5902 if (fieldx < 1 || fieldx > 256 ||
5903 fieldy < 1 || fieldy > 256 ||
5904 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5906 level->no_valid_file = TRUE;
5908 Warn("cannot decode level from stream -- using empty level");
5913 // maximum envelope header size is 31 bytes
5914 envelope_header_len = header[envelope_header_pos];
5915 // maximum envelope content size is 110 (156?) bytes
5916 envelope_content_len = header[envelope_content_pos];
5918 // maximum level title size is 40 bytes
5919 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5920 // maximum level author size is 30 (51?) bytes
5921 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5925 for (i = 0; i < envelope_header_len; i++)
5926 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5927 level->envelope[0].text[envelope_size++] =
5928 header[envelope_header_pos + 1 + i];
5930 if (envelope_header_len > 0 && envelope_content_len > 0)
5932 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5933 level->envelope[0].text[envelope_size++] = '\n';
5934 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5935 level->envelope[0].text[envelope_size++] = '\n';
5938 for (i = 0; i < envelope_content_len; i++)
5939 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5940 level->envelope[0].text[envelope_size++] =
5941 header[envelope_content_pos + 1 + i];
5943 level->envelope[0].text[envelope_size] = '\0';
5945 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5946 level->envelope[0].ysize = 10;
5947 level->envelope[0].autowrap = TRUE;
5948 level->envelope[0].centered = TRUE;
5950 for (i = 0; i < level_name_len; i++)
5951 level->name[i] = header[level_name_pos + 1 + i];
5952 level->name[level_name_len] = '\0';
5954 for (i = 0; i < level_author_len; i++)
5955 level->author[i] = header[level_author_pos + 1 + i];
5956 level->author[level_author_len] = '\0';
5958 num_yamyam_contents = header[60] | (header[61] << 8);
5959 level->num_yamyam_contents =
5960 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5962 for (i = 0; i < num_yamyam_contents; i++)
5964 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5966 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5967 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5969 if (i < MAX_ELEMENT_CONTENTS)
5970 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5974 fieldx = header[6] | (header[7] << 8);
5975 fieldy = header[8] | (header[9] << 8);
5976 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5977 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5979 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5981 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5982 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5984 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5985 level->field[x][y] = getMappedElement_DC(element_dc);
5988 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5989 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5990 level->field[x][y] = EL_PLAYER_1;
5992 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5993 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5994 level->field[x][y] = EL_PLAYER_2;
5996 level->gems_needed = header[18] | (header[19] << 8);
5998 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5999 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6000 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6001 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6002 level->score[SC_NUT] = header[28] | (header[29] << 8);
6003 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6004 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6005 level->score[SC_BUG] = header[34] | (header[35] << 8);
6006 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6007 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6008 level->score[SC_KEY] = header[40] | (header[41] << 8);
6009 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6011 level->time = header[44] | (header[45] << 8);
6013 level->amoeba_speed = header[46] | (header[47] << 8);
6014 level->time_light = header[48] | (header[49] << 8);
6015 level->time_timegate = header[50] | (header[51] << 8);
6016 level->time_wheel = header[52] | (header[53] << 8);
6017 level->time_magic_wall = header[54] | (header[55] << 8);
6018 level->extra_time = header[56] | (header[57] << 8);
6019 level->shield_normal_time = header[58] | (header[59] << 8);
6021 // shield and extra time elements do not have a score
6022 level->score[SC_SHIELD] = 0;
6023 level->extra_time_score = 0;
6025 // set time for normal and deadly shields to the same value
6026 level->shield_deadly_time = level->shield_normal_time;
6028 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6029 // can slip down from flat walls, like normal walls and steel walls
6030 level->em_slippery_gems = TRUE;
6032 // time score is counted for each 10 seconds left in Diamond Caves levels
6033 level->time_score_base = 10;
6036 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6037 struct LevelFileInfo *level_file_info,
6038 boolean level_info_only)
6040 char *filename = level_file_info->filename;
6042 int num_magic_bytes = 8;
6043 char magic_bytes[num_magic_bytes + 1];
6044 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6046 if (!(file = openFile(filename, MODE_READ)))
6048 level->no_valid_file = TRUE;
6050 if (!level_info_only)
6051 Warn("cannot read level '%s' -- using empty level", filename);
6056 // fseek(file, 0x0000, SEEK_SET);
6058 if (level_file_info->packed)
6060 // read "magic bytes" from start of file
6061 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6062 magic_bytes[0] = '\0';
6064 // check "magic bytes" for correct file format
6065 if (!strPrefix(magic_bytes, "DC2"))
6067 level->no_valid_file = TRUE;
6069 Warn("unknown DC level file '%s' -- using empty level", filename);
6074 if (strPrefix(magic_bytes, "DC2Win95") ||
6075 strPrefix(magic_bytes, "DC2Win98"))
6077 int position_first_level = 0x00fa;
6078 int extra_bytes = 4;
6081 // advance file stream to first level inside the level package
6082 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6084 // each block of level data is followed by block of non-level data
6085 num_levels_to_skip *= 2;
6087 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6088 while (num_levels_to_skip >= 0)
6090 // advance file stream to next level inside the level package
6091 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6093 level->no_valid_file = TRUE;
6095 Warn("cannot fseek in file '%s' -- using empty level", filename);
6100 // skip apparently unused extra bytes following each level
6101 ReadUnusedBytesFromFile(file, extra_bytes);
6103 // read size of next level in level package
6104 skip_bytes = getFile32BitLE(file);
6106 num_levels_to_skip--;
6111 level->no_valid_file = TRUE;
6113 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6119 LoadLevelFromFileStream_DC(file, level);
6125 // ----------------------------------------------------------------------------
6126 // functions for loading SB level
6127 // ----------------------------------------------------------------------------
6129 int getMappedElement_SB(int element_ascii, boolean use_ces)
6137 sb_element_mapping[] =
6139 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6140 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6141 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6142 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6143 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6144 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6145 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6146 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6153 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6154 if (element_ascii == sb_element_mapping[i].ascii)
6155 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6157 return EL_UNDEFINED;
6160 static void SetLevelSettings_SB(struct LevelInfo *level)
6164 level->use_step_counter = TRUE;
6167 level->score[SC_TIME_BONUS] = 0;
6168 level->time_score_base = 1;
6169 level->rate_time_over_score = TRUE;
6172 level->auto_exit_sokoban = TRUE;
6175 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6176 struct LevelFileInfo *level_file_info,
6177 boolean level_info_only)
6179 char *filename = level_file_info->filename;
6180 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6181 char last_comment[MAX_LINE_LEN];
6182 char level_name[MAX_LINE_LEN];
6185 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6186 boolean read_continued_line = FALSE;
6187 boolean reading_playfield = FALSE;
6188 boolean got_valid_playfield_line = FALSE;
6189 boolean invalid_playfield_char = FALSE;
6190 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6191 int file_level_nr = 0;
6193 int x = 0, y = 0; // initialized to make compilers happy
6195 last_comment[0] = '\0';
6196 level_name[0] = '\0';
6198 if (!(file = openFile(filename, MODE_READ)))
6200 level->no_valid_file = TRUE;
6202 if (!level_info_only)
6203 Warn("cannot read level '%s' -- using empty level", filename);
6208 while (!checkEndOfFile(file))
6210 // level successfully read, but next level may follow here
6211 if (!got_valid_playfield_line && reading_playfield)
6213 // read playfield from single level file -- skip remaining file
6214 if (!level_file_info->packed)
6217 if (file_level_nr >= num_levels_to_skip)
6222 last_comment[0] = '\0';
6223 level_name[0] = '\0';
6225 reading_playfield = FALSE;
6228 got_valid_playfield_line = FALSE;
6230 // read next line of input file
6231 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6234 // check if line was completely read and is terminated by line break
6235 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6238 // cut trailing line break (this can be newline and/or carriage return)
6239 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6240 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6243 // copy raw input line for later use (mainly debugging output)
6244 strcpy(line_raw, line);
6246 if (read_continued_line)
6248 // append new line to existing line, if there is enough space
6249 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6250 strcat(previous_line, line_ptr);
6252 strcpy(line, previous_line); // copy storage buffer to line
6254 read_continued_line = FALSE;
6257 // if the last character is '\', continue at next line
6258 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6260 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6261 strcpy(previous_line, line); // copy line to storage buffer
6263 read_continued_line = TRUE;
6269 if (line[0] == '\0')
6272 // extract comment text from comment line
6275 for (line_ptr = line; *line_ptr; line_ptr++)
6276 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6279 strcpy(last_comment, line_ptr);
6284 // extract level title text from line containing level title
6285 if (line[0] == '\'')
6287 strcpy(level_name, &line[1]);
6289 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6290 level_name[strlen(level_name) - 1] = '\0';
6295 // skip lines containing only spaces (or empty lines)
6296 for (line_ptr = line; *line_ptr; line_ptr++)
6297 if (*line_ptr != ' ')
6299 if (*line_ptr == '\0')
6302 // at this point, we have found a line containing part of a playfield
6304 got_valid_playfield_line = TRUE;
6306 if (!reading_playfield)
6308 reading_playfield = TRUE;
6309 invalid_playfield_char = FALSE;
6311 for (x = 0; x < MAX_LEV_FIELDX; x++)
6312 for (y = 0; y < MAX_LEV_FIELDY; y++)
6313 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6318 // start with topmost tile row
6322 // skip playfield line if larger row than allowed
6323 if (y >= MAX_LEV_FIELDY)
6326 // start with leftmost tile column
6329 // read playfield elements from line
6330 for (line_ptr = line; *line_ptr; line_ptr++)
6332 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6334 // stop parsing playfield line if larger column than allowed
6335 if (x >= MAX_LEV_FIELDX)
6338 if (mapped_sb_element == EL_UNDEFINED)
6340 invalid_playfield_char = TRUE;
6345 level->field[x][y] = mapped_sb_element;
6347 // continue with next tile column
6350 level->fieldx = MAX(x, level->fieldx);
6353 if (invalid_playfield_char)
6355 // if first playfield line, treat invalid lines as comment lines
6357 reading_playfield = FALSE;
6362 // continue with next tile row
6370 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6371 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6373 if (!reading_playfield)
6375 level->no_valid_file = TRUE;
6377 Warn("cannot read level '%s' -- using empty level", filename);
6382 if (*level_name != '\0')
6384 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6385 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6387 else if (*last_comment != '\0')
6389 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6390 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6394 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6397 // set all empty fields beyond the border walls to invisible steel wall
6398 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6400 if ((x == 0 || x == level->fieldx - 1 ||
6401 y == 0 || y == level->fieldy - 1) &&
6402 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6403 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6404 level->field, level->fieldx, level->fieldy);
6407 // set special level settings for Sokoban levels
6408 SetLevelSettings_SB(level);
6410 if (load_xsb_to_ces)
6412 // special global settings can now be set in level template
6413 level->use_custom_template = TRUE;
6418 // -------------------------------------------------------------------------
6419 // functions for handling native levels
6420 // -------------------------------------------------------------------------
6422 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6423 struct LevelFileInfo *level_file_info,
6424 boolean level_info_only)
6426 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6427 level->no_valid_file = TRUE;
6430 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6431 struct LevelFileInfo *level_file_info,
6432 boolean level_info_only)
6436 // determine position of requested level inside level package
6437 if (level_file_info->packed)
6438 pos = level_file_info->nr - leveldir_current->first_level;
6440 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6441 level->no_valid_file = TRUE;
6444 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6445 struct LevelFileInfo *level_file_info,
6446 boolean level_info_only)
6448 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6449 level->no_valid_file = TRUE;
6452 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6454 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6455 CopyNativeLevel_RND_to_BD(level);
6456 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6457 CopyNativeLevel_RND_to_EM(level);
6458 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6459 CopyNativeLevel_RND_to_SP(level);
6460 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6461 CopyNativeLevel_RND_to_MM(level);
6464 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6466 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6467 CopyNativeLevel_BD_to_RND(level);
6468 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6469 CopyNativeLevel_EM_to_RND(level);
6470 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6471 CopyNativeLevel_SP_to_RND(level);
6472 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6473 CopyNativeLevel_MM_to_RND(level);
6476 void SaveNativeLevel(struct LevelInfo *level)
6478 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6480 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6481 char *filename = getLevelFilenameFromBasename(basename);
6483 CopyNativeLevel_RND_to_SP(level);
6484 CopyNativeTape_RND_to_SP(level);
6486 SaveNativeLevel_SP(filename);
6491 // ----------------------------------------------------------------------------
6492 // functions for loading generic level
6493 // ----------------------------------------------------------------------------
6495 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6496 struct LevelFileInfo *level_file_info,
6497 boolean level_info_only)
6499 // always start with reliable default values
6500 setLevelInfoToDefaults(level, level_info_only, TRUE);
6502 switch (level_file_info->type)
6504 case LEVEL_FILE_TYPE_RND:
6505 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6508 case LEVEL_FILE_TYPE_EM:
6509 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6510 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6513 case LEVEL_FILE_TYPE_SP:
6514 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6515 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6518 case LEVEL_FILE_TYPE_MM:
6519 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6520 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6523 case LEVEL_FILE_TYPE_DC:
6524 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6527 case LEVEL_FILE_TYPE_SB:
6528 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6532 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6536 // if level file is invalid, restore level structure to default values
6537 if (level->no_valid_file)
6538 setLevelInfoToDefaults(level, level_info_only, FALSE);
6540 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6541 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6543 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6544 CopyNativeLevel_Native_to_RND(level);
6547 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6549 static struct LevelFileInfo level_file_info;
6551 // always start with reliable default values
6552 setFileInfoToDefaults(&level_file_info);
6554 level_file_info.nr = 0; // unknown level number
6555 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6557 setString(&level_file_info.filename, filename);
6559 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6562 static void LoadLevel_InitVersion(struct LevelInfo *level)
6566 if (leveldir_current == NULL) // only when dumping level
6569 // all engine modifications also valid for levels which use latest engine
6570 if (level->game_version < VERSION_IDENT(3,2,0,5))
6572 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6573 level->time_score_base = 10;
6576 if (leveldir_current->latest_engine)
6578 // ---------- use latest game engine --------------------------------------
6580 /* For all levels which are forced to use the latest game engine version
6581 (normally all but user contributed, private and undefined levels), set
6582 the game engine version to the actual version; this allows for actual
6583 corrections in the game engine to take effect for existing, converted
6584 levels (from "classic" or other existing games) to make the emulation
6585 of the corresponding game more accurate, while (hopefully) not breaking
6586 existing levels created from other players. */
6588 level->game_version = GAME_VERSION_ACTUAL;
6590 /* Set special EM style gems behaviour: EM style gems slip down from
6591 normal, steel and growing wall. As this is a more fundamental change,
6592 it seems better to set the default behaviour to "off" (as it is more
6593 natural) and make it configurable in the level editor (as a property
6594 of gem style elements). Already existing converted levels (neither
6595 private nor contributed levels) are changed to the new behaviour. */
6597 if (level->file_version < FILE_VERSION_2_0)
6598 level->em_slippery_gems = TRUE;
6603 // ---------- use game engine the level was created with --------------------
6605 /* For all levels which are not forced to use the latest game engine
6606 version (normally user contributed, private and undefined levels),
6607 use the version of the game engine the levels were created for.
6609 Since 2.0.1, the game engine version is now directly stored
6610 in the level file (chunk "VERS"), so there is no need anymore
6611 to set the game version from the file version (except for old,
6612 pre-2.0 levels, where the game version is still taken from the
6613 file format version used to store the level -- see above). */
6615 // player was faster than enemies in 1.0.0 and before
6616 if (level->file_version == FILE_VERSION_1_0)
6617 for (i = 0; i < MAX_PLAYERS; i++)
6618 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6620 // default behaviour for EM style gems was "slippery" only in 2.0.1
6621 if (level->game_version == VERSION_IDENT(2,0,1,0))
6622 level->em_slippery_gems = TRUE;
6624 // springs could be pushed over pits before (pre-release version) 2.2.0
6625 if (level->game_version < VERSION_IDENT(2,2,0,0))
6626 level->use_spring_bug = TRUE;
6628 if (level->game_version < VERSION_IDENT(3,2,0,5))
6630 // time orb caused limited time in endless time levels before 3.2.0-5
6631 level->use_time_orb_bug = TRUE;
6633 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6634 level->block_snap_field = FALSE;
6636 // extra time score was same value as time left score before 3.2.0-5
6637 level->extra_time_score = level->score[SC_TIME_BONUS];
6640 if (level->game_version < VERSION_IDENT(3,2,0,7))
6642 // default behaviour for snapping was "not continuous" before 3.2.0-7
6643 level->continuous_snapping = FALSE;
6646 // only few elements were able to actively move into acid before 3.1.0
6647 // trigger settings did not exist before 3.1.0; set to default "any"
6648 if (level->game_version < VERSION_IDENT(3,1,0,0))
6650 // correct "can move into acid" settings (all zero in old levels)
6652 level->can_move_into_acid_bits = 0; // nothing can move into acid
6653 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6655 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6656 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6657 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6658 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6660 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6661 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6663 // correct trigger settings (stored as zero == "none" in old levels)
6665 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6667 int element = EL_CUSTOM_START + i;
6668 struct ElementInfo *ei = &element_info[element];
6670 for (j = 0; j < ei->num_change_pages; j++)
6672 struct ElementChangeInfo *change = &ei->change_page[j];
6674 change->trigger_player = CH_PLAYER_ANY;
6675 change->trigger_page = CH_PAGE_ANY;
6680 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6682 int element = EL_CUSTOM_256;
6683 struct ElementInfo *ei = &element_info[element];
6684 struct ElementChangeInfo *change = &ei->change_page[0];
6686 /* This is needed to fix a problem that was caused by a bugfix in function
6687 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6688 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6689 not replace walkable elements, but instead just placed the player on it,
6690 without placing the Sokoban field under the player). Unfortunately, this
6691 breaks "Snake Bite" style levels when the snake is halfway through a door
6692 that just closes (the snake head is still alive and can be moved in this
6693 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6694 player (without Sokoban element) which then gets killed as designed). */
6696 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6697 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6698 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6699 change->target_element = EL_PLAYER_1;
6702 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6703 if (level->game_version < VERSION_IDENT(3,2,5,0))
6705 /* This is needed to fix a problem that was caused by a bugfix in function
6706 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6707 corrects the behaviour when a custom element changes to another custom
6708 element with a higher element number that has change actions defined.
6709 Normally, only one change per frame is allowed for custom elements.
6710 Therefore, it is checked if a custom element already changed in the
6711 current frame; if it did, subsequent changes are suppressed.
6712 Unfortunately, this is only checked for element changes, but not for
6713 change actions, which are still executed. As the function above loops
6714 through all custom elements from lower to higher, an element change
6715 resulting in a lower CE number won't be checked again, while a target
6716 element with a higher number will also be checked, and potential change
6717 actions will get executed for this CE, too (which is wrong), while
6718 further changes are ignored (which is correct). As this bugfix breaks
6719 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6720 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6721 behaviour for existing levels and tapes that make use of this bug */
6723 level->use_action_after_change_bug = TRUE;
6726 // not centering level after relocating player was default only in 3.2.3
6727 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6728 level->shifted_relocation = TRUE;
6730 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6731 if (level->game_version < VERSION_IDENT(3,2,6,0))
6732 level->em_explodes_by_fire = TRUE;
6734 // levels were solved by the first player entering an exit up to 4.1.0.0
6735 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6736 level->solved_by_one_player = TRUE;
6738 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6739 if (level->game_version < VERSION_IDENT(4,1,1,1))
6740 level->use_life_bugs = TRUE;
6742 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6743 if (level->game_version < VERSION_IDENT(4,1,1,1))
6744 level->sb_objects_needed = FALSE;
6746 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6747 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6748 level->finish_dig_collect = FALSE;
6750 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6751 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6752 level->keep_walkable_ce = TRUE;
6755 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6757 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6760 // check if this level is (not) a Sokoban level
6761 for (y = 0; y < level->fieldy; y++)
6762 for (x = 0; x < level->fieldx; x++)
6763 if (!IS_SB_ELEMENT(Tile[x][y]))
6764 is_sokoban_level = FALSE;
6766 if (is_sokoban_level)
6768 // set special level settings for Sokoban levels
6769 SetLevelSettings_SB(level);
6773 static void LoadLevel_InitSettings(struct LevelInfo *level)
6775 // adjust level settings for (non-native) Sokoban-style levels
6776 LoadLevel_InitSettings_SB(level);
6778 // rename levels with title "nameless level" or if renaming is forced
6779 if (leveldir_current->empty_level_name != NULL &&
6780 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6781 leveldir_current->force_level_name))
6782 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6783 leveldir_current->empty_level_name, level_nr);
6786 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6790 // map elements that have changed in newer versions
6791 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6792 level->game_version);
6793 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6794 for (x = 0; x < 3; x++)
6795 for (y = 0; y < 3; y++)
6796 level->yamyam_content[i].e[x][y] =
6797 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6798 level->game_version);
6802 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6806 // map custom element change events that have changed in newer versions
6807 // (these following values were accidentally changed in version 3.0.1)
6808 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6809 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6811 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6813 int element = EL_CUSTOM_START + i;
6815 // order of checking and copying events to be mapped is important
6816 // (do not change the start and end value -- they are constant)
6817 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6819 if (HAS_CHANGE_EVENT(element, j - 2))
6821 SET_CHANGE_EVENT(element, j - 2, FALSE);
6822 SET_CHANGE_EVENT(element, j, TRUE);
6826 // order of checking and copying events to be mapped is important
6827 // (do not change the start and end value -- they are constant)
6828 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6830 if (HAS_CHANGE_EVENT(element, j - 1))
6832 SET_CHANGE_EVENT(element, j - 1, FALSE);
6833 SET_CHANGE_EVENT(element, j, TRUE);
6839 // initialize "can_change" field for old levels with only one change page
6840 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6842 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6844 int element = EL_CUSTOM_START + i;
6846 if (CAN_CHANGE(element))
6847 element_info[element].change->can_change = TRUE;
6851 // correct custom element values (for old levels without these options)
6852 if (level->game_version < VERSION_IDENT(3,1,1,0))
6854 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6856 int element = EL_CUSTOM_START + i;
6857 struct ElementInfo *ei = &element_info[element];
6859 if (ei->access_direction == MV_NO_DIRECTION)
6860 ei->access_direction = MV_ALL_DIRECTIONS;
6864 // correct custom element values (fix invalid values for all versions)
6867 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6869 int element = EL_CUSTOM_START + i;
6870 struct ElementInfo *ei = &element_info[element];
6872 for (j = 0; j < ei->num_change_pages; j++)
6874 struct ElementChangeInfo *change = &ei->change_page[j];
6876 if (change->trigger_player == CH_PLAYER_NONE)
6877 change->trigger_player = CH_PLAYER_ANY;
6879 if (change->trigger_side == CH_SIDE_NONE)
6880 change->trigger_side = CH_SIDE_ANY;
6885 // initialize "can_explode" field for old levels which did not store this
6886 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6887 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6889 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6891 int element = EL_CUSTOM_START + i;
6893 if (EXPLODES_1X1_OLD(element))
6894 element_info[element].explosion_type = EXPLODES_1X1;
6896 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6897 EXPLODES_SMASHED(element) ||
6898 EXPLODES_IMPACT(element)));
6902 // correct previously hard-coded move delay values for maze runner style
6903 if (level->game_version < VERSION_IDENT(3,1,1,0))
6905 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6907 int element = EL_CUSTOM_START + i;
6909 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6911 // previously hard-coded and therefore ignored
6912 element_info[element].move_delay_fixed = 9;
6913 element_info[element].move_delay_random = 0;
6918 // set some other uninitialized values of custom elements in older levels
6919 if (level->game_version < VERSION_IDENT(3,1,0,0))
6921 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6923 int element = EL_CUSTOM_START + i;
6925 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6927 element_info[element].explosion_delay = 17;
6928 element_info[element].ignition_delay = 8;
6932 // set mouse click change events to work for left/middle/right mouse button
6933 if (level->game_version < VERSION_IDENT(4,2,3,0))
6935 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6937 int element = EL_CUSTOM_START + i;
6938 struct ElementInfo *ei = &element_info[element];
6940 for (j = 0; j < ei->num_change_pages; j++)
6942 struct ElementChangeInfo *change = &ei->change_page[j];
6944 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6945 change->has_event[CE_PRESSED_BY_MOUSE] ||
6946 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6947 change->has_event[CE_MOUSE_PRESSED_ON_X])
6948 change->trigger_side = CH_SIDE_ANY;
6954 static void LoadLevel_InitElements(struct LevelInfo *level)
6956 LoadLevel_InitStandardElements(level);
6958 if (level->file_has_custom_elements)
6959 LoadLevel_InitCustomElements(level);
6961 // initialize element properties for level editor etc.
6962 InitElementPropertiesEngine(level->game_version);
6963 InitElementPropertiesGfxElement();
6966 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6970 // map elements that have changed in newer versions
6971 for (y = 0; y < level->fieldy; y++)
6972 for (x = 0; x < level->fieldx; x++)
6973 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6974 level->game_version);
6976 // clear unused playfield data (nicer if level gets resized in editor)
6977 for (x = 0; x < MAX_LEV_FIELDX; x++)
6978 for (y = 0; y < MAX_LEV_FIELDY; y++)
6979 if (x >= level->fieldx || y >= level->fieldy)
6980 level->field[x][y] = EL_EMPTY;
6982 // copy elements to runtime playfield array
6983 for (x = 0; x < MAX_LEV_FIELDX; x++)
6984 for (y = 0; y < MAX_LEV_FIELDY; y++)
6985 Tile[x][y] = level->field[x][y];
6987 // initialize level size variables for faster access
6988 lev_fieldx = level->fieldx;
6989 lev_fieldy = level->fieldy;
6991 // determine border element for this level
6992 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6993 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6998 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7000 struct LevelFileInfo *level_file_info = &level->file_info;
7002 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7003 CopyNativeLevel_RND_to_Native(level);
7006 static void LoadLevelTemplate_LoadAndInit(void)
7008 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7010 LoadLevel_InitVersion(&level_template);
7011 LoadLevel_InitElements(&level_template);
7012 LoadLevel_InitSettings(&level_template);
7014 ActivateLevelTemplate();
7017 void LoadLevelTemplate(int nr)
7019 if (!fileExists(getGlobalLevelTemplateFilename()))
7021 Warn("no level template found for this level");
7026 setLevelFileInfo(&level_template.file_info, nr);
7028 LoadLevelTemplate_LoadAndInit();
7031 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7033 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7035 LoadLevelTemplate_LoadAndInit();
7038 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7040 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7042 if (level.use_custom_template)
7044 if (network_level != NULL)
7045 LoadNetworkLevelTemplate(network_level);
7047 LoadLevelTemplate(-1);
7050 LoadLevel_InitVersion(&level);
7051 LoadLevel_InitElements(&level);
7052 LoadLevel_InitPlayfield(&level);
7053 LoadLevel_InitSettings(&level);
7055 LoadLevel_InitNativeEngines(&level);
7058 void LoadLevel(int nr)
7060 SetLevelSetInfo(leveldir_current->identifier, nr);
7062 setLevelFileInfo(&level.file_info, nr);
7064 LoadLevel_LoadAndInit(NULL);
7067 void LoadLevelInfoOnly(int nr)
7069 setLevelFileInfo(&level.file_info, nr);
7071 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7074 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7076 SetLevelSetInfo(network_level->leveldir_identifier,
7077 network_level->file_info.nr);
7079 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7081 LoadLevel_LoadAndInit(network_level);
7084 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7088 chunk_size += putFileVersion(file, level->file_version);
7089 chunk_size += putFileVersion(file, level->game_version);
7094 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7098 chunk_size += putFile16BitBE(file, level->creation_date.year);
7099 chunk_size += putFile8Bit(file, level->creation_date.month);
7100 chunk_size += putFile8Bit(file, level->creation_date.day);
7105 #if ENABLE_HISTORIC_CHUNKS
7106 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7110 putFile8Bit(file, level->fieldx);
7111 putFile8Bit(file, level->fieldy);
7113 putFile16BitBE(file, level->time);
7114 putFile16BitBE(file, level->gems_needed);
7116 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7117 putFile8Bit(file, level->name[i]);
7119 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7120 putFile8Bit(file, level->score[i]);
7122 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7123 for (y = 0; y < 3; y++)
7124 for (x = 0; x < 3; x++)
7125 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7126 level->yamyam_content[i].e[x][y]));
7127 putFile8Bit(file, level->amoeba_speed);
7128 putFile8Bit(file, level->time_magic_wall);
7129 putFile8Bit(file, level->time_wheel);
7130 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7131 level->amoeba_content));
7132 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7133 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7134 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7135 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7137 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7139 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7140 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7141 putFile32BitBE(file, level->can_move_into_acid_bits);
7142 putFile8Bit(file, level->dont_collide_with_bits);
7144 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7145 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7147 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7148 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7149 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7151 putFile8Bit(file, level->game_engine_type);
7153 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7157 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7162 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7163 chunk_size += putFile8Bit(file, level->name[i]);
7168 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7173 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7174 chunk_size += putFile8Bit(file, level->author[i]);
7179 #if ENABLE_HISTORIC_CHUNKS
7180 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7185 for (y = 0; y < level->fieldy; y++)
7186 for (x = 0; x < level->fieldx; x++)
7187 if (level->encoding_16bit_field)
7188 chunk_size += putFile16BitBE(file, level->field[x][y]);
7190 chunk_size += putFile8Bit(file, level->field[x][y]);
7196 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7201 for (y = 0; y < level->fieldy; y++)
7202 for (x = 0; x < level->fieldx; x++)
7203 chunk_size += putFile16BitBE(file, level->field[x][y]);
7208 #if ENABLE_HISTORIC_CHUNKS
7209 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7213 putFile8Bit(file, EL_YAMYAM);
7214 putFile8Bit(file, level->num_yamyam_contents);
7215 putFile8Bit(file, 0);
7216 putFile8Bit(file, 0);
7218 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7219 for (y = 0; y < 3; y++)
7220 for (x = 0; x < 3; x++)
7221 if (level->encoding_16bit_field)
7222 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7224 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7228 #if ENABLE_HISTORIC_CHUNKS
7229 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7232 int num_contents, content_xsize, content_ysize;
7233 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7235 if (element == EL_YAMYAM)
7237 num_contents = level->num_yamyam_contents;
7241 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7242 for (y = 0; y < 3; y++)
7243 for (x = 0; x < 3; x++)
7244 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7246 else if (element == EL_BD_AMOEBA)
7252 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7253 for (y = 0; y < 3; y++)
7254 for (x = 0; x < 3; x++)
7255 content_array[i][x][y] = EL_EMPTY;
7256 content_array[0][0][0] = level->amoeba_content;
7260 // chunk header already written -- write empty chunk data
7261 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7263 Warn("cannot save content for element '%d'", element);
7268 putFile16BitBE(file, element);
7269 putFile8Bit(file, num_contents);
7270 putFile8Bit(file, content_xsize);
7271 putFile8Bit(file, content_ysize);
7273 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7275 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7276 for (y = 0; y < 3; y++)
7277 for (x = 0; x < 3; x++)
7278 putFile16BitBE(file, content_array[i][x][y]);
7282 #if ENABLE_HISTORIC_CHUNKS
7283 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7285 int envelope_nr = element - EL_ENVELOPE_1;
7286 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7290 chunk_size += putFile16BitBE(file, element);
7291 chunk_size += putFile16BitBE(file, envelope_len);
7292 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7293 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7295 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7296 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7298 for (i = 0; i < envelope_len; i++)
7299 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7305 #if ENABLE_HISTORIC_CHUNKS
7306 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7307 int num_changed_custom_elements)
7311 putFile16BitBE(file, num_changed_custom_elements);
7313 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7315 int element = EL_CUSTOM_START + i;
7317 struct ElementInfo *ei = &element_info[element];
7319 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7321 if (check < num_changed_custom_elements)
7323 putFile16BitBE(file, element);
7324 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7331 if (check != num_changed_custom_elements) // should not happen
7332 Warn("inconsistent number of custom element properties");
7336 #if ENABLE_HISTORIC_CHUNKS
7337 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7338 int num_changed_custom_elements)
7342 putFile16BitBE(file, num_changed_custom_elements);
7344 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7346 int element = EL_CUSTOM_START + i;
7348 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7350 if (check < num_changed_custom_elements)
7352 putFile16BitBE(file, element);
7353 putFile16BitBE(file, element_info[element].change->target_element);
7360 if (check != num_changed_custom_elements) // should not happen
7361 Warn("inconsistent number of custom target elements");
7365 #if ENABLE_HISTORIC_CHUNKS
7366 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7367 int num_changed_custom_elements)
7369 int i, j, x, y, check = 0;
7371 putFile16BitBE(file, num_changed_custom_elements);
7373 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7375 int element = EL_CUSTOM_START + i;
7376 struct ElementInfo *ei = &element_info[element];
7378 if (ei->modified_settings)
7380 if (check < num_changed_custom_elements)
7382 putFile16BitBE(file, element);
7384 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7385 putFile8Bit(file, ei->description[j]);
7387 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7389 // some free bytes for future properties and padding
7390 WriteUnusedBytesToFile(file, 7);
7392 putFile8Bit(file, ei->use_gfx_element);
7393 putFile16BitBE(file, ei->gfx_element_initial);
7395 putFile8Bit(file, ei->collect_score_initial);
7396 putFile8Bit(file, ei->collect_count_initial);
7398 putFile16BitBE(file, ei->push_delay_fixed);
7399 putFile16BitBE(file, ei->push_delay_random);
7400 putFile16BitBE(file, ei->move_delay_fixed);
7401 putFile16BitBE(file, ei->move_delay_random);
7403 putFile16BitBE(file, ei->move_pattern);
7404 putFile8Bit(file, ei->move_direction_initial);
7405 putFile8Bit(file, ei->move_stepsize);
7407 for (y = 0; y < 3; y++)
7408 for (x = 0; x < 3; x++)
7409 putFile16BitBE(file, ei->content.e[x][y]);
7411 putFile32BitBE(file, ei->change->events);
7413 putFile16BitBE(file, ei->change->target_element);
7415 putFile16BitBE(file, ei->change->delay_fixed);
7416 putFile16BitBE(file, ei->change->delay_random);
7417 putFile16BitBE(file, ei->change->delay_frames);
7419 putFile16BitBE(file, ei->change->initial_trigger_element);
7421 putFile8Bit(file, ei->change->explode);
7422 putFile8Bit(file, ei->change->use_target_content);
7423 putFile8Bit(file, ei->change->only_if_complete);
7424 putFile8Bit(file, ei->change->use_random_replace);
7426 putFile8Bit(file, ei->change->random_percentage);
7427 putFile8Bit(file, ei->change->replace_when);
7429 for (y = 0; y < 3; y++)
7430 for (x = 0; x < 3; x++)
7431 putFile16BitBE(file, ei->change->content.e[x][y]);
7433 putFile8Bit(file, ei->slippery_type);
7435 // some free bytes for future properties and padding
7436 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7443 if (check != num_changed_custom_elements) // should not happen
7444 Warn("inconsistent number of custom element properties");
7448 #if ENABLE_HISTORIC_CHUNKS
7449 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7451 struct ElementInfo *ei = &element_info[element];
7454 // ---------- custom element base property values (96 bytes) ----------------
7456 putFile16BitBE(file, element);
7458 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7459 putFile8Bit(file, ei->description[i]);
7461 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7463 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7465 putFile8Bit(file, ei->num_change_pages);
7467 putFile16BitBE(file, ei->ce_value_fixed_initial);
7468 putFile16BitBE(file, ei->ce_value_random_initial);
7469 putFile8Bit(file, ei->use_last_ce_value);
7471 putFile8Bit(file, ei->use_gfx_element);
7472 putFile16BitBE(file, ei->gfx_element_initial);
7474 putFile8Bit(file, ei->collect_score_initial);
7475 putFile8Bit(file, ei->collect_count_initial);
7477 putFile8Bit(file, ei->drop_delay_fixed);
7478 putFile8Bit(file, ei->push_delay_fixed);
7479 putFile8Bit(file, ei->drop_delay_random);
7480 putFile8Bit(file, ei->push_delay_random);
7481 putFile16BitBE(file, ei->move_delay_fixed);
7482 putFile16BitBE(file, ei->move_delay_random);
7484 // bits 0 - 15 of "move_pattern" ...
7485 putFile16BitBE(file, ei->move_pattern & 0xffff);
7486 putFile8Bit(file, ei->move_direction_initial);
7487 putFile8Bit(file, ei->move_stepsize);
7489 putFile8Bit(file, ei->slippery_type);
7491 for (y = 0; y < 3; y++)
7492 for (x = 0; x < 3; x++)
7493 putFile16BitBE(file, ei->content.e[x][y]);
7495 putFile16BitBE(file, ei->move_enter_element);
7496 putFile16BitBE(file, ei->move_leave_element);
7497 putFile8Bit(file, ei->move_leave_type);
7499 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7500 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7502 putFile8Bit(file, ei->access_direction);
7504 putFile8Bit(file, ei->explosion_delay);
7505 putFile8Bit(file, ei->ignition_delay);
7506 putFile8Bit(file, ei->explosion_type);
7508 // some free bytes for future custom property values and padding
7509 WriteUnusedBytesToFile(file, 1);
7511 // ---------- change page property values (48 bytes) ------------------------
7513 for (i = 0; i < ei->num_change_pages; i++)
7515 struct ElementChangeInfo *change = &ei->change_page[i];
7516 unsigned int event_bits;
7518 // bits 0 - 31 of "has_event[]" ...
7520 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7521 if (change->has_event[j])
7522 event_bits |= (1u << j);
7523 putFile32BitBE(file, event_bits);
7525 putFile16BitBE(file, change->target_element);
7527 putFile16BitBE(file, change->delay_fixed);
7528 putFile16BitBE(file, change->delay_random);
7529 putFile16BitBE(file, change->delay_frames);
7531 putFile16BitBE(file, change->initial_trigger_element);
7533 putFile8Bit(file, change->explode);
7534 putFile8Bit(file, change->use_target_content);
7535 putFile8Bit(file, change->only_if_complete);
7536 putFile8Bit(file, change->use_random_replace);
7538 putFile8Bit(file, change->random_percentage);
7539 putFile8Bit(file, change->replace_when);
7541 for (y = 0; y < 3; y++)
7542 for (x = 0; x < 3; x++)
7543 putFile16BitBE(file, change->target_content.e[x][y]);
7545 putFile8Bit(file, change->can_change);
7547 putFile8Bit(file, change->trigger_side);
7549 putFile8Bit(file, change->trigger_player);
7550 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7551 log_2(change->trigger_page)));
7553 putFile8Bit(file, change->has_action);
7554 putFile8Bit(file, change->action_type);
7555 putFile8Bit(file, change->action_mode);
7556 putFile16BitBE(file, change->action_arg);
7558 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7560 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7561 if (change->has_event[j])
7562 event_bits |= (1u << (j - 32));
7563 putFile8Bit(file, event_bits);
7568 #if ENABLE_HISTORIC_CHUNKS
7569 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7571 struct ElementInfo *ei = &element_info[element];
7572 struct ElementGroupInfo *group = ei->group;
7575 putFile16BitBE(file, element);
7577 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7578 putFile8Bit(file, ei->description[i]);
7580 putFile8Bit(file, group->num_elements);
7582 putFile8Bit(file, ei->use_gfx_element);
7583 putFile16BitBE(file, ei->gfx_element_initial);
7585 putFile8Bit(file, group->choice_mode);
7587 // some free bytes for future values and padding
7588 WriteUnusedBytesToFile(file, 3);
7590 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7591 putFile16BitBE(file, group->element[i]);
7595 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7596 boolean write_element)
7598 int save_type = entry->save_type;
7599 int data_type = entry->data_type;
7600 int conf_type = entry->conf_type;
7601 int byte_mask = conf_type & CONF_MASK_BYTES;
7602 int element = entry->element;
7603 int default_value = entry->default_value;
7605 boolean modified = FALSE;
7607 if (byte_mask != CONF_MASK_MULTI_BYTES)
7609 void *value_ptr = entry->value;
7610 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7613 // check if any settings have been modified before saving them
7614 if (value != default_value)
7617 // do not save if explicitly told or if unmodified default settings
7618 if ((save_type == SAVE_CONF_NEVER) ||
7619 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7623 num_bytes += putFile16BitBE(file, element);
7625 num_bytes += putFile8Bit(file, conf_type);
7626 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7627 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7628 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7631 else if (data_type == TYPE_STRING)
7633 char *default_string = entry->default_string;
7634 char *string = (char *)(entry->value);
7635 int string_length = strlen(string);
7638 // check if any settings have been modified before saving them
7639 if (!strEqual(string, default_string))
7642 // do not save if explicitly told or if unmodified default settings
7643 if ((save_type == SAVE_CONF_NEVER) ||
7644 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7648 num_bytes += putFile16BitBE(file, element);
7650 num_bytes += putFile8Bit(file, conf_type);
7651 num_bytes += putFile16BitBE(file, string_length);
7653 for (i = 0; i < string_length; i++)
7654 num_bytes += putFile8Bit(file, string[i]);
7656 else if (data_type == TYPE_ELEMENT_LIST)
7658 int *element_array = (int *)(entry->value);
7659 int num_elements = *(int *)(entry->num_entities);
7662 // check if any settings have been modified before saving them
7663 for (i = 0; i < num_elements; i++)
7664 if (element_array[i] != default_value)
7667 // do not save if explicitly told or if unmodified default settings
7668 if ((save_type == SAVE_CONF_NEVER) ||
7669 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7673 num_bytes += putFile16BitBE(file, element);
7675 num_bytes += putFile8Bit(file, conf_type);
7676 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7678 for (i = 0; i < num_elements; i++)
7679 num_bytes += putFile16BitBE(file, element_array[i]);
7681 else if (data_type == TYPE_CONTENT_LIST)
7683 struct Content *content = (struct Content *)(entry->value);
7684 int num_contents = *(int *)(entry->num_entities);
7687 // check if any settings have been modified before saving them
7688 for (i = 0; i < num_contents; i++)
7689 for (y = 0; y < 3; y++)
7690 for (x = 0; x < 3; x++)
7691 if (content[i].e[x][y] != default_value)
7694 // do not save if explicitly told or if unmodified default settings
7695 if ((save_type == SAVE_CONF_NEVER) ||
7696 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7700 num_bytes += putFile16BitBE(file, element);
7702 num_bytes += putFile8Bit(file, conf_type);
7703 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7705 for (i = 0; i < num_contents; i++)
7706 for (y = 0; y < 3; y++)
7707 for (x = 0; x < 3; x++)
7708 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7714 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7719 li = *level; // copy level data into temporary buffer
7721 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7722 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7727 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7732 li = *level; // copy level data into temporary buffer
7734 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7735 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7740 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7742 int envelope_nr = element - EL_ENVELOPE_1;
7746 chunk_size += putFile16BitBE(file, element);
7748 // copy envelope data into temporary buffer
7749 xx_envelope = level->envelope[envelope_nr];
7751 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7752 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7757 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7759 struct ElementInfo *ei = &element_info[element];
7763 chunk_size += putFile16BitBE(file, element);
7765 xx_ei = *ei; // copy element data into temporary buffer
7767 // set default description string for this specific element
7768 strcpy(xx_default_description, getDefaultElementDescription(ei));
7770 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7771 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7773 for (i = 0; i < ei->num_change_pages; i++)
7775 struct ElementChangeInfo *change = &ei->change_page[i];
7777 xx_current_change_page = i;
7779 xx_change = *change; // copy change data into temporary buffer
7782 setEventBitsFromEventFlags(change);
7784 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7785 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7792 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7794 struct ElementInfo *ei = &element_info[element];
7795 struct ElementGroupInfo *group = ei->group;
7799 chunk_size += putFile16BitBE(file, element);
7801 xx_ei = *ei; // copy element data into temporary buffer
7802 xx_group = *group; // copy group data into temporary buffer
7804 // set default description string for this specific element
7805 strcpy(xx_default_description, getDefaultElementDescription(ei));
7807 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7808 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7813 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7815 struct ElementInfo *ei = &element_info[element];
7819 chunk_size += putFile16BitBE(file, element);
7821 xx_ei = *ei; // copy element data into temporary buffer
7823 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7824 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7829 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7830 boolean save_as_template)
7836 if (!(file = fopen(filename, MODE_WRITE)))
7838 Warn("cannot save level file '%s'", filename);
7843 level->file_version = FILE_VERSION_ACTUAL;
7844 level->game_version = GAME_VERSION_ACTUAL;
7846 level->creation_date = getCurrentDate();
7848 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7849 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7851 chunk_size = SaveLevel_VERS(NULL, level);
7852 putFileChunkBE(file, "VERS", chunk_size);
7853 SaveLevel_VERS(file, level);
7855 chunk_size = SaveLevel_DATE(NULL, level);
7856 putFileChunkBE(file, "DATE", chunk_size);
7857 SaveLevel_DATE(file, level);
7859 chunk_size = SaveLevel_NAME(NULL, level);
7860 putFileChunkBE(file, "NAME", chunk_size);
7861 SaveLevel_NAME(file, level);
7863 chunk_size = SaveLevel_AUTH(NULL, level);
7864 putFileChunkBE(file, "AUTH", chunk_size);
7865 SaveLevel_AUTH(file, level);
7867 chunk_size = SaveLevel_INFO(NULL, level);
7868 putFileChunkBE(file, "INFO", chunk_size);
7869 SaveLevel_INFO(file, level);
7871 chunk_size = SaveLevel_BODY(NULL, level);
7872 putFileChunkBE(file, "BODY", chunk_size);
7873 SaveLevel_BODY(file, level);
7875 chunk_size = SaveLevel_ELEM(NULL, level);
7876 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7878 putFileChunkBE(file, "ELEM", chunk_size);
7879 SaveLevel_ELEM(file, level);
7882 for (i = 0; i < NUM_ENVELOPES; i++)
7884 int element = EL_ENVELOPE_1 + i;
7886 chunk_size = SaveLevel_NOTE(NULL, level, element);
7887 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7889 putFileChunkBE(file, "NOTE", chunk_size);
7890 SaveLevel_NOTE(file, level, element);
7894 // if not using template level, check for non-default custom/group elements
7895 if (!level->use_custom_template || save_as_template)
7897 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7899 int element = EL_CUSTOM_START + i;
7901 chunk_size = SaveLevel_CUSX(NULL, level, element);
7902 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7904 putFileChunkBE(file, "CUSX", chunk_size);
7905 SaveLevel_CUSX(file, level, element);
7909 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7911 int element = EL_GROUP_START + i;
7913 chunk_size = SaveLevel_GRPX(NULL, level, element);
7914 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7916 putFileChunkBE(file, "GRPX", chunk_size);
7917 SaveLevel_GRPX(file, level, element);
7921 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7923 int element = GET_EMPTY_ELEMENT(i);
7925 chunk_size = SaveLevel_EMPX(NULL, level, element);
7926 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7928 putFileChunkBE(file, "EMPX", chunk_size);
7929 SaveLevel_EMPX(file, level, element);
7936 SetFilePermissions(filename, PERMS_PRIVATE);
7939 void SaveLevel(int nr)
7941 char *filename = getDefaultLevelFilename(nr);
7943 SaveLevelFromFilename(&level, filename, FALSE);
7946 void SaveLevelTemplate(void)
7948 char *filename = getLocalLevelTemplateFilename();
7950 SaveLevelFromFilename(&level, filename, TRUE);
7953 boolean SaveLevelChecked(int nr)
7955 char *filename = getDefaultLevelFilename(nr);
7956 boolean new_level = !fileExists(filename);
7957 boolean level_saved = FALSE;
7959 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7964 Request("Level saved!", REQ_CONFIRM);
7972 void DumpLevel(struct LevelInfo *level)
7974 if (level->no_level_file || level->no_valid_file)
7976 Warn("cannot dump -- no valid level file found");
7982 Print("Level xxx (file version %08d, game version %08d)\n",
7983 level->file_version, level->game_version);
7986 Print("Level author: '%s'\n", level->author);
7987 Print("Level title: '%s'\n", level->name);
7989 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7991 Print("Level time: %d seconds\n", level->time);
7992 Print("Gems needed: %d\n", level->gems_needed);
7994 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7995 Print("Time for wheel: %d seconds\n", level->time_wheel);
7996 Print("Time for light: %d seconds\n", level->time_light);
7997 Print("Time for timegate: %d seconds\n", level->time_timegate);
7999 Print("Amoeba speed: %d\n", level->amoeba_speed);
8002 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8003 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8004 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8005 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8006 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8007 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8013 for (i = 0; i < NUM_ENVELOPES; i++)
8015 char *text = level->envelope[i].text;
8016 int text_len = strlen(text);
8017 boolean has_text = FALSE;
8019 for (j = 0; j < text_len; j++)
8020 if (text[j] != ' ' && text[j] != '\n')
8026 Print("Envelope %d:\n'%s'\n", i + 1, text);
8034 void DumpLevels(void)
8036 static LevelDirTree *dumplevel_leveldir = NULL;
8038 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8039 global.dumplevel_leveldir);
8041 if (dumplevel_leveldir == NULL)
8042 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8044 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8045 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8046 Fail("no such level number: %d", global.dumplevel_level_nr);
8048 leveldir_current = dumplevel_leveldir;
8050 LoadLevel(global.dumplevel_level_nr);
8057 // ============================================================================
8058 // tape file functions
8059 // ============================================================================
8061 static void setTapeInfoToDefaults(void)
8065 // always start with reliable default values (empty tape)
8068 // default values (also for pre-1.2 tapes) with only the first player
8069 tape.player_participates[0] = TRUE;
8070 for (i = 1; i < MAX_PLAYERS; i++)
8071 tape.player_participates[i] = FALSE;
8073 // at least one (default: the first) player participates in every tape
8074 tape.num_participating_players = 1;
8076 tape.property_bits = TAPE_PROPERTY_NONE;
8078 tape.level_nr = level_nr;
8080 tape.changed = FALSE;
8081 tape.solved = FALSE;
8083 tape.recording = FALSE;
8084 tape.playing = FALSE;
8085 tape.pausing = FALSE;
8087 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8088 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8090 tape.no_info_chunk = TRUE;
8091 tape.no_valid_file = FALSE;
8094 static int getTapePosSize(struct TapeInfo *tape)
8096 int tape_pos_size = 0;
8098 if (tape->use_key_actions)
8099 tape_pos_size += tape->num_participating_players;
8101 if (tape->use_mouse_actions)
8102 tape_pos_size += 3; // x and y position and mouse button mask
8104 tape_pos_size += 1; // tape action delay value
8106 return tape_pos_size;
8109 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8111 tape->use_key_actions = FALSE;
8112 tape->use_mouse_actions = FALSE;
8114 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8115 tape->use_key_actions = TRUE;
8117 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8118 tape->use_mouse_actions = TRUE;
8121 static int getTapeActionValue(struct TapeInfo *tape)
8123 return (tape->use_key_actions &&
8124 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8125 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8126 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8127 TAPE_ACTIONS_DEFAULT);
8130 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8132 tape->file_version = getFileVersion(file);
8133 tape->game_version = getFileVersion(file);
8138 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8142 tape->random_seed = getFile32BitBE(file);
8143 tape->date = getFile32BitBE(file);
8144 tape->length = getFile32BitBE(file);
8146 // read header fields that are new since version 1.2
8147 if (tape->file_version >= FILE_VERSION_1_2)
8149 byte store_participating_players = getFile8Bit(file);
8152 // since version 1.2, tapes store which players participate in the tape
8153 tape->num_participating_players = 0;
8154 for (i = 0; i < MAX_PLAYERS; i++)
8156 tape->player_participates[i] = FALSE;
8158 if (store_participating_players & (1 << i))
8160 tape->player_participates[i] = TRUE;
8161 tape->num_participating_players++;
8165 setTapeActionFlags(tape, getFile8Bit(file));
8167 tape->property_bits = getFile8Bit(file);
8168 tape->solved = getFile8Bit(file);
8170 engine_version = getFileVersion(file);
8171 if (engine_version > 0)
8172 tape->engine_version = engine_version;
8174 tape->engine_version = tape->game_version;
8180 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8182 tape->scr_fieldx = getFile8Bit(file);
8183 tape->scr_fieldy = getFile8Bit(file);
8188 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8190 char *level_identifier = NULL;
8191 int level_identifier_size;
8194 tape->no_info_chunk = FALSE;
8196 level_identifier_size = getFile16BitBE(file);
8198 level_identifier = checked_malloc(level_identifier_size);
8200 for (i = 0; i < level_identifier_size; i++)
8201 level_identifier[i] = getFile8Bit(file);
8203 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8204 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8206 checked_free(level_identifier);
8208 tape->level_nr = getFile16BitBE(file);
8210 chunk_size = 2 + level_identifier_size + 2;
8215 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8218 int tape_pos_size = getTapePosSize(tape);
8219 int chunk_size_expected = tape_pos_size * tape->length;
8221 if (chunk_size_expected != chunk_size)
8223 ReadUnusedBytesFromFile(file, chunk_size);
8224 return chunk_size_expected;
8227 for (i = 0; i < tape->length; i++)
8229 if (i >= MAX_TAPE_LEN)
8231 Warn("tape truncated -- size exceeds maximum tape size %d",
8234 // tape too large; read and ignore remaining tape data from this chunk
8235 for (;i < tape->length; i++)
8236 ReadUnusedBytesFromFile(file, tape_pos_size);
8241 if (tape->use_key_actions)
8243 for (j = 0; j < MAX_PLAYERS; j++)
8245 tape->pos[i].action[j] = MV_NONE;
8247 if (tape->player_participates[j])
8248 tape->pos[i].action[j] = getFile8Bit(file);
8252 if (tape->use_mouse_actions)
8254 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8255 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8256 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8259 tape->pos[i].delay = getFile8Bit(file);
8261 if (tape->file_version == FILE_VERSION_1_0)
8263 // eliminate possible diagonal moves in old tapes
8264 // this is only for backward compatibility
8266 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8267 byte action = tape->pos[i].action[0];
8268 int k, num_moves = 0;
8270 for (k = 0; k < 4; k++)
8272 if (action & joy_dir[k])
8274 tape->pos[i + num_moves].action[0] = joy_dir[k];
8276 tape->pos[i + num_moves].delay = 0;
8285 tape->length += num_moves;
8288 else if (tape->file_version < FILE_VERSION_2_0)
8290 // convert pre-2.0 tapes to new tape format
8292 if (tape->pos[i].delay > 1)
8295 tape->pos[i + 1] = tape->pos[i];
8296 tape->pos[i + 1].delay = 1;
8299 for (j = 0; j < MAX_PLAYERS; j++)
8300 tape->pos[i].action[j] = MV_NONE;
8301 tape->pos[i].delay--;
8308 if (checkEndOfFile(file))
8312 if (i != tape->length)
8313 chunk_size = tape_pos_size * i;
8318 static void LoadTape_SokobanSolution(char *filename)
8321 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8323 if (!(file = openFile(filename, MODE_READ)))
8325 tape.no_valid_file = TRUE;
8330 while (!checkEndOfFile(file))
8332 unsigned char c = getByteFromFile(file);
8334 if (checkEndOfFile(file))
8341 tape.pos[tape.length].action[0] = MV_UP;
8342 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8348 tape.pos[tape.length].action[0] = MV_DOWN;
8349 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8355 tape.pos[tape.length].action[0] = MV_LEFT;
8356 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8362 tape.pos[tape.length].action[0] = MV_RIGHT;
8363 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8371 // ignore white-space characters
8375 tape.no_valid_file = TRUE;
8377 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8385 if (tape.no_valid_file)
8388 tape.length_frames = GetTapeLengthFrames();
8389 tape.length_seconds = GetTapeLengthSeconds();
8392 void LoadTapeFromFilename(char *filename)
8394 char cookie[MAX_LINE_LEN];
8395 char chunk_name[CHUNK_ID_LEN + 1];
8399 // always start with reliable default values
8400 setTapeInfoToDefaults();
8402 if (strSuffix(filename, ".sln"))
8404 LoadTape_SokobanSolution(filename);
8409 if (!(file = openFile(filename, MODE_READ)))
8411 tape.no_valid_file = TRUE;
8416 getFileChunkBE(file, chunk_name, NULL);
8417 if (strEqual(chunk_name, "RND1"))
8419 getFile32BitBE(file); // not used
8421 getFileChunkBE(file, chunk_name, NULL);
8422 if (!strEqual(chunk_name, "TAPE"))
8424 tape.no_valid_file = TRUE;
8426 Warn("unknown format of tape file '%s'", filename);
8433 else // check for pre-2.0 file format with cookie string
8435 strcpy(cookie, chunk_name);
8436 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8438 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8439 cookie[strlen(cookie) - 1] = '\0';
8441 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8443 tape.no_valid_file = TRUE;
8445 Warn("unknown format of tape file '%s'", filename);
8452 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8454 tape.no_valid_file = TRUE;
8456 Warn("unsupported version of tape file '%s'", filename);
8463 // pre-2.0 tape files have no game version, so use file version here
8464 tape.game_version = tape.file_version;
8467 if (tape.file_version < FILE_VERSION_1_2)
8469 // tape files from versions before 1.2.0 without chunk structure
8470 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8471 LoadTape_BODY(file, 2 * tape.length, &tape);
8479 int (*loader)(File *, int, struct TapeInfo *);
8483 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8484 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8485 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8486 { "INFO", -1, LoadTape_INFO },
8487 { "BODY", -1, LoadTape_BODY },
8491 while (getFileChunkBE(file, chunk_name, &chunk_size))
8495 while (chunk_info[i].name != NULL &&
8496 !strEqual(chunk_name, chunk_info[i].name))
8499 if (chunk_info[i].name == NULL)
8501 Warn("unknown chunk '%s' in tape file '%s'",
8502 chunk_name, filename);
8504 ReadUnusedBytesFromFile(file, chunk_size);
8506 else if (chunk_info[i].size != -1 &&
8507 chunk_info[i].size != chunk_size)
8509 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8510 chunk_size, chunk_name, filename);
8512 ReadUnusedBytesFromFile(file, chunk_size);
8516 // call function to load this tape chunk
8517 int chunk_size_expected =
8518 (chunk_info[i].loader)(file, chunk_size, &tape);
8520 // the size of some chunks cannot be checked before reading other
8521 // chunks first (like "HEAD" and "BODY") that contain some header
8522 // information, so check them here
8523 if (chunk_size_expected != chunk_size)
8525 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8526 chunk_size, chunk_name, filename);
8534 tape.length_frames = GetTapeLengthFrames();
8535 tape.length_seconds = GetTapeLengthSeconds();
8538 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8540 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8542 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8543 tape.engine_version);
8547 void LoadTape(int nr)
8549 char *filename = getTapeFilename(nr);
8551 LoadTapeFromFilename(filename);
8554 void LoadSolutionTape(int nr)
8556 char *filename = getSolutionTapeFilename(nr);
8558 LoadTapeFromFilename(filename);
8560 if (TAPE_IS_EMPTY(tape))
8562 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8563 level.native_bd_level->replay != NULL)
8564 CopyNativeTape_BD_to_RND(&level);
8565 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8566 level.native_sp_level->demo.is_available)
8567 CopyNativeTape_SP_to_RND(&level);
8571 void LoadScoreTape(char *score_tape_basename, int nr)
8573 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8575 LoadTapeFromFilename(filename);
8578 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8580 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8582 LoadTapeFromFilename(filename);
8585 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8587 // chunk required for team mode tapes with non-default screen size
8588 return (tape->num_participating_players > 1 &&
8589 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8590 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8593 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8595 putFileVersion(file, tape->file_version);
8596 putFileVersion(file, tape->game_version);
8599 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8602 byte store_participating_players = 0;
8604 // set bits for participating players for compact storage
8605 for (i = 0; i < MAX_PLAYERS; i++)
8606 if (tape->player_participates[i])
8607 store_participating_players |= (1 << i);
8609 putFile32BitBE(file, tape->random_seed);
8610 putFile32BitBE(file, tape->date);
8611 putFile32BitBE(file, tape->length);
8613 putFile8Bit(file, store_participating_players);
8615 putFile8Bit(file, getTapeActionValue(tape));
8617 putFile8Bit(file, tape->property_bits);
8618 putFile8Bit(file, tape->solved);
8620 putFileVersion(file, tape->engine_version);
8623 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8625 putFile8Bit(file, tape->scr_fieldx);
8626 putFile8Bit(file, tape->scr_fieldy);
8629 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8631 int level_identifier_size = strlen(tape->level_identifier) + 1;
8634 putFile16BitBE(file, level_identifier_size);
8636 for (i = 0; i < level_identifier_size; i++)
8637 putFile8Bit(file, tape->level_identifier[i]);
8639 putFile16BitBE(file, tape->level_nr);
8642 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8646 for (i = 0; i < tape->length; i++)
8648 if (tape->use_key_actions)
8650 for (j = 0; j < MAX_PLAYERS; j++)
8651 if (tape->player_participates[j])
8652 putFile8Bit(file, tape->pos[i].action[j]);
8655 if (tape->use_mouse_actions)
8657 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8658 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8659 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8662 putFile8Bit(file, tape->pos[i].delay);
8666 void SaveTapeToFilename(char *filename)
8670 int info_chunk_size;
8671 int body_chunk_size;
8673 if (!(file = fopen(filename, MODE_WRITE)))
8675 Warn("cannot save level recording file '%s'", filename);
8680 tape_pos_size = getTapePosSize(&tape);
8682 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8683 body_chunk_size = tape_pos_size * tape.length;
8685 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8686 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8688 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8689 SaveTape_VERS(file, &tape);
8691 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8692 SaveTape_HEAD(file, &tape);
8694 if (checkSaveTape_SCRN(&tape))
8696 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8697 SaveTape_SCRN(file, &tape);
8700 putFileChunkBE(file, "INFO", info_chunk_size);
8701 SaveTape_INFO(file, &tape);
8703 putFileChunkBE(file, "BODY", body_chunk_size);
8704 SaveTape_BODY(file, &tape);
8708 SetFilePermissions(filename, PERMS_PRIVATE);
8711 static void SaveTapeExt(char *filename)
8715 tape.file_version = FILE_VERSION_ACTUAL;
8716 tape.game_version = GAME_VERSION_ACTUAL;
8718 tape.num_participating_players = 0;
8720 // count number of participating players
8721 for (i = 0; i < MAX_PLAYERS; i++)
8722 if (tape.player_participates[i])
8723 tape.num_participating_players++;
8725 SaveTapeToFilename(filename);
8727 tape.changed = FALSE;
8730 void SaveTape(int nr)
8732 char *filename = getTapeFilename(nr);
8734 InitTapeDirectory(leveldir_current->subdir);
8736 SaveTapeExt(filename);
8739 void SaveScoreTape(int nr)
8741 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8743 // used instead of "leveldir_current->subdir" (for network games)
8744 InitScoreTapeDirectory(levelset.identifier, nr);
8746 SaveTapeExt(filename);
8749 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8750 unsigned int req_state_added)
8752 char *filename = getTapeFilename(nr);
8753 boolean new_tape = !fileExists(filename);
8754 boolean tape_saved = FALSE;
8756 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8761 Request(msg_saved, REQ_CONFIRM | req_state_added);
8769 boolean SaveTapeChecked(int nr)
8771 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8774 boolean SaveTapeChecked_LevelSolved(int nr)
8776 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8777 "Level solved! Tape saved!", REQ_STAY_OPEN);
8780 void DumpTape(struct TapeInfo *tape)
8782 int tape_frame_counter;
8785 if (tape->no_valid_file)
8787 Warn("cannot dump -- no valid tape file found");
8794 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8795 tape->level_nr, tape->file_version, tape->game_version);
8796 Print(" (effective engine version %08d)\n",
8797 tape->engine_version);
8798 Print("Level series identifier: '%s'\n", tape->level_identifier);
8800 Print("Solution tape: %s\n",
8801 tape->solved ? "yes" :
8802 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8804 Print("Special tape properties: ");
8805 if (tape->property_bits == TAPE_PROPERTY_NONE)
8807 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8808 Print("[em_random_bug]");
8809 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8810 Print("[game_speed]");
8811 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8813 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8814 Print("[single_step]");
8815 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8816 Print("[snapshot]");
8817 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8818 Print("[replayed]");
8819 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8820 Print("[tas_keys]");
8821 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8822 Print("[small_graphics]");
8825 int year2 = tape->date / 10000;
8826 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8827 int month_index_raw = (tape->date / 100) % 100;
8828 int month_index = month_index_raw % 12; // prevent invalid index
8829 int month = month_index + 1;
8830 int day = tape->date % 100;
8832 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8836 tape_frame_counter = 0;
8838 for (i = 0; i < tape->length; i++)
8840 if (i >= MAX_TAPE_LEN)
8845 for (j = 0; j < MAX_PLAYERS; j++)
8847 if (tape->player_participates[j])
8849 int action = tape->pos[i].action[j];
8851 Print("%d:%02x ", j, action);
8852 Print("[%c%c%c%c|%c%c] - ",
8853 (action & JOY_LEFT ? '<' : ' '),
8854 (action & JOY_RIGHT ? '>' : ' '),
8855 (action & JOY_UP ? '^' : ' '),
8856 (action & JOY_DOWN ? 'v' : ' '),
8857 (action & JOY_BUTTON_1 ? '1' : ' '),
8858 (action & JOY_BUTTON_2 ? '2' : ' '));
8862 Print("(%03d) ", tape->pos[i].delay);
8863 Print("[%05d]\n", tape_frame_counter);
8865 tape_frame_counter += tape->pos[i].delay;
8871 void DumpTapes(void)
8873 static LevelDirTree *dumptape_leveldir = NULL;
8875 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8876 global.dumptape_leveldir);
8878 if (dumptape_leveldir == NULL)
8879 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8881 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8882 global.dumptape_level_nr > dumptape_leveldir->last_level)
8883 Fail("no such level number: %d", global.dumptape_level_nr);
8885 leveldir_current = dumptape_leveldir;
8887 if (options.mytapes)
8888 LoadTape(global.dumptape_level_nr);
8890 LoadSolutionTape(global.dumptape_level_nr);
8898 // ============================================================================
8899 // score file functions
8900 // ============================================================================
8902 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8906 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8908 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8909 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8910 scores->entry[i].score = 0;
8911 scores->entry[i].time = 0;
8913 scores->entry[i].id = -1;
8914 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8915 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8916 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8917 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8918 strcpy(scores->entry[i].country_code, "??");
8921 scores->num_entries = 0;
8922 scores->last_added = -1;
8923 scores->last_added_local = -1;
8925 scores->updated = FALSE;
8926 scores->uploaded = FALSE;
8927 scores->tape_downloaded = FALSE;
8928 scores->force_last_added = FALSE;
8930 // The following values are intentionally not reset here:
8934 // - continue_playing
8935 // - continue_on_return
8938 static void setScoreInfoToDefaults(void)
8940 setScoreInfoToDefaultsExt(&scores);
8943 static void setServerScoreInfoToDefaults(void)
8945 setScoreInfoToDefaultsExt(&server_scores);
8948 static void LoadScore_OLD(int nr)
8951 char *filename = getScoreFilename(nr);
8952 char cookie[MAX_LINE_LEN];
8953 char line[MAX_LINE_LEN];
8957 if (!(file = fopen(filename, MODE_READ)))
8960 // check file identifier
8961 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8963 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8964 cookie[strlen(cookie) - 1] = '\0';
8966 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8968 Warn("unknown format of score file '%s'", filename);
8975 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8977 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8978 Warn("fscanf() failed; %s", strerror(errno));
8980 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8983 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8984 line[strlen(line) - 1] = '\0';
8986 for (line_ptr = line; *line_ptr; line_ptr++)
8988 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8990 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8991 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9000 static void ConvertScore_OLD(void)
9002 // only convert score to time for levels that rate playing time over score
9003 if (!level.rate_time_over_score)
9006 // convert old score to playing time for score-less levels (like Supaplex)
9007 int time_final_max = 999;
9010 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9012 int score = scores.entry[i].score;
9014 if (score > 0 && score < time_final_max)
9015 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9019 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9021 scores->file_version = getFileVersion(file);
9022 scores->game_version = getFileVersion(file);
9027 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9029 char *level_identifier = NULL;
9030 int level_identifier_size;
9033 level_identifier_size = getFile16BitBE(file);
9035 level_identifier = checked_malloc(level_identifier_size);
9037 for (i = 0; i < level_identifier_size; i++)
9038 level_identifier[i] = getFile8Bit(file);
9040 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9041 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9043 checked_free(level_identifier);
9045 scores->level_nr = getFile16BitBE(file);
9046 scores->num_entries = getFile16BitBE(file);
9048 chunk_size = 2 + level_identifier_size + 2 + 2;
9053 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9057 for (i = 0; i < scores->num_entries; i++)
9059 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9060 scores->entry[i].name[j] = getFile8Bit(file);
9062 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9065 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9070 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9074 for (i = 0; i < scores->num_entries; i++)
9075 scores->entry[i].score = getFile16BitBE(file);
9077 chunk_size = scores->num_entries * 2;
9082 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9086 for (i = 0; i < scores->num_entries; i++)
9087 scores->entry[i].score = getFile32BitBE(file);
9089 chunk_size = scores->num_entries * 4;
9094 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9098 for (i = 0; i < scores->num_entries; i++)
9099 scores->entry[i].time = getFile32BitBE(file);
9101 chunk_size = scores->num_entries * 4;
9106 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9110 for (i = 0; i < scores->num_entries; i++)
9112 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9113 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9115 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9118 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9123 void LoadScore(int nr)
9125 char *filename = getScoreFilename(nr);
9126 char cookie[MAX_LINE_LEN];
9127 char chunk_name[CHUNK_ID_LEN + 1];
9129 boolean old_score_file_format = FALSE;
9132 // always start with reliable default values
9133 setScoreInfoToDefaults();
9135 if (!(file = openFile(filename, MODE_READ)))
9138 getFileChunkBE(file, chunk_name, NULL);
9139 if (strEqual(chunk_name, "RND1"))
9141 getFile32BitBE(file); // not used
9143 getFileChunkBE(file, chunk_name, NULL);
9144 if (!strEqual(chunk_name, "SCOR"))
9146 Warn("unknown format of score file '%s'", filename);
9153 else // check for old file format with cookie string
9155 strcpy(cookie, chunk_name);
9156 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9158 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9159 cookie[strlen(cookie) - 1] = '\0';
9161 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9163 Warn("unknown format of score file '%s'", filename);
9170 old_score_file_format = TRUE;
9173 if (old_score_file_format)
9175 // score files from versions before 4.2.4.0 without chunk structure
9178 // convert score to time, if possible (mainly for Supaplex levels)
9187 int (*loader)(File *, int, struct ScoreInfo *);
9191 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9192 { "INFO", -1, LoadScore_INFO },
9193 { "NAME", -1, LoadScore_NAME },
9194 { "SCOR", -1, LoadScore_SCOR },
9195 { "SC4R", -1, LoadScore_SC4R },
9196 { "TIME", -1, LoadScore_TIME },
9197 { "TAPE", -1, LoadScore_TAPE },
9202 while (getFileChunkBE(file, chunk_name, &chunk_size))
9206 while (chunk_info[i].name != NULL &&
9207 !strEqual(chunk_name, chunk_info[i].name))
9210 if (chunk_info[i].name == NULL)
9212 Warn("unknown chunk '%s' in score file '%s'",
9213 chunk_name, filename);
9215 ReadUnusedBytesFromFile(file, chunk_size);
9217 else if (chunk_info[i].size != -1 &&
9218 chunk_info[i].size != chunk_size)
9220 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9221 chunk_size, chunk_name, filename);
9223 ReadUnusedBytesFromFile(file, chunk_size);
9227 // call function to load this score chunk
9228 int chunk_size_expected =
9229 (chunk_info[i].loader)(file, chunk_size, &scores);
9231 // the size of some chunks cannot be checked before reading other
9232 // chunks first (like "HEAD" and "BODY") that contain some header
9233 // information, so check them here
9234 if (chunk_size_expected != chunk_size)
9236 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9237 chunk_size, chunk_name, filename);
9246 #if ENABLE_HISTORIC_CHUNKS
9247 void SaveScore_OLD(int nr)
9250 char *filename = getScoreFilename(nr);
9253 // used instead of "leveldir_current->subdir" (for network games)
9254 InitScoreDirectory(levelset.identifier);
9256 if (!(file = fopen(filename, MODE_WRITE)))
9258 Warn("cannot save score for level %d", nr);
9263 fprintf(file, "%s\n\n", SCORE_COOKIE);
9265 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9266 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9270 SetFilePermissions(filename, PERMS_PRIVATE);
9274 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9276 putFileVersion(file, scores->file_version);
9277 putFileVersion(file, scores->game_version);
9280 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9282 int level_identifier_size = strlen(scores->level_identifier) + 1;
9285 putFile16BitBE(file, level_identifier_size);
9287 for (i = 0; i < level_identifier_size; i++)
9288 putFile8Bit(file, scores->level_identifier[i]);
9290 putFile16BitBE(file, scores->level_nr);
9291 putFile16BitBE(file, scores->num_entries);
9294 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9298 for (i = 0; i < scores->num_entries; i++)
9300 int name_size = strlen(scores->entry[i].name);
9302 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9303 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9307 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9311 for (i = 0; i < scores->num_entries; i++)
9312 putFile16BitBE(file, scores->entry[i].score);
9315 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9319 for (i = 0; i < scores->num_entries; i++)
9320 putFile32BitBE(file, scores->entry[i].score);
9323 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9327 for (i = 0; i < scores->num_entries; i++)
9328 putFile32BitBE(file, scores->entry[i].time);
9331 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9335 for (i = 0; i < scores->num_entries; i++)
9337 int size = strlen(scores->entry[i].tape_basename);
9339 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9340 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9344 static void SaveScoreToFilename(char *filename)
9347 int info_chunk_size;
9348 int name_chunk_size;
9349 int scor_chunk_size;
9350 int sc4r_chunk_size;
9351 int time_chunk_size;
9352 int tape_chunk_size;
9353 boolean has_large_score_values;
9356 if (!(file = fopen(filename, MODE_WRITE)))
9358 Warn("cannot save score file '%s'", filename);
9363 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9364 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9365 scor_chunk_size = scores.num_entries * 2;
9366 sc4r_chunk_size = scores.num_entries * 4;
9367 time_chunk_size = scores.num_entries * 4;
9368 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9370 has_large_score_values = FALSE;
9371 for (i = 0; i < scores.num_entries; i++)
9372 if (scores.entry[i].score > 0xffff)
9373 has_large_score_values = TRUE;
9375 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9376 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9378 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9379 SaveScore_VERS(file, &scores);
9381 putFileChunkBE(file, "INFO", info_chunk_size);
9382 SaveScore_INFO(file, &scores);
9384 putFileChunkBE(file, "NAME", name_chunk_size);
9385 SaveScore_NAME(file, &scores);
9387 if (has_large_score_values)
9389 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9390 SaveScore_SC4R(file, &scores);
9394 putFileChunkBE(file, "SCOR", scor_chunk_size);
9395 SaveScore_SCOR(file, &scores);
9398 putFileChunkBE(file, "TIME", time_chunk_size);
9399 SaveScore_TIME(file, &scores);
9401 putFileChunkBE(file, "TAPE", tape_chunk_size);
9402 SaveScore_TAPE(file, &scores);
9406 SetFilePermissions(filename, PERMS_PRIVATE);
9409 void SaveScore(int nr)
9411 char *filename = getScoreFilename(nr);
9414 // used instead of "leveldir_current->subdir" (for network games)
9415 InitScoreDirectory(levelset.identifier);
9417 scores.file_version = FILE_VERSION_ACTUAL;
9418 scores.game_version = GAME_VERSION_ACTUAL;
9420 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9421 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9422 scores.level_nr = level_nr;
9424 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9425 if (scores.entry[i].score == 0 &&
9426 scores.entry[i].time == 0 &&
9427 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9430 scores.num_entries = i;
9432 if (scores.num_entries == 0)
9435 SaveScoreToFilename(filename);
9438 static void LoadServerScoreFromCache(int nr)
9440 struct ScoreEntry score_entry;
9449 { &score_entry.score, FALSE, 0 },
9450 { &score_entry.time, FALSE, 0 },
9451 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9452 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9453 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9454 { &score_entry.id, FALSE, 0 },
9455 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9456 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9457 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9458 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9462 char *filename = getScoreCacheFilename(nr);
9463 SetupFileHash *score_hash = loadSetupFileHash(filename);
9466 server_scores.num_entries = 0;
9468 if (score_hash == NULL)
9471 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9473 score_entry = server_scores.entry[i];
9475 for (j = 0; score_mapping[j].value != NULL; j++)
9479 sprintf(token, "%02d.%d", i, j);
9481 char *value = getHashEntry(score_hash, token);
9486 if (score_mapping[j].is_string)
9488 char *score_value = (char *)score_mapping[j].value;
9489 int value_size = score_mapping[j].string_size;
9491 strncpy(score_value, value, value_size);
9492 score_value[value_size] = '\0';
9496 int *score_value = (int *)score_mapping[j].value;
9498 *score_value = atoi(value);
9501 server_scores.num_entries = i + 1;
9504 server_scores.entry[i] = score_entry;
9507 freeSetupFileHash(score_hash);
9510 void LoadServerScore(int nr, boolean download_score)
9512 if (!setup.use_api_server)
9515 // always start with reliable default values
9516 setServerScoreInfoToDefaults();
9518 // 1st step: load server scores from cache file (which may not exist)
9519 // (this should prevent reading it while the thread is writing to it)
9520 LoadServerScoreFromCache(nr);
9522 if (download_score && runtime.use_api_server)
9524 // 2nd step: download server scores from score server to cache file
9525 // (as thread, as it might time out if the server is not reachable)
9526 ApiGetScoreAsThread(nr);
9530 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9532 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9534 // if score tape not uploaded, ask for uploading missing tapes later
9535 if (!setup.has_remaining_tapes)
9536 setup.ask_for_remaining_tapes = TRUE;
9538 setup.provide_uploading_tapes = TRUE;
9539 setup.has_remaining_tapes = TRUE;
9541 SaveSetup_ServerSetup();
9544 void SaveServerScore(int nr, boolean tape_saved)
9546 if (!runtime.use_api_server)
9548 PrepareScoreTapesForUpload(leveldir_current->subdir);
9553 ApiAddScoreAsThread(nr, tape_saved, NULL);
9556 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9557 char *score_tape_filename)
9559 if (!runtime.use_api_server)
9562 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9565 void LoadLocalAndServerScore(int nr, boolean download_score)
9567 int last_added_local = scores.last_added_local;
9568 boolean force_last_added = scores.force_last_added;
9570 // needed if only showing server scores
9571 setScoreInfoToDefaults();
9573 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9576 // restore last added local score entry (before merging server scores)
9577 scores.last_added = scores.last_added_local = last_added_local;
9579 if (setup.use_api_server &&
9580 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9582 // load server scores from cache file and trigger update from server
9583 LoadServerScore(nr, download_score);
9585 // merge local scores with scores from server
9589 if (force_last_added)
9590 scores.force_last_added = force_last_added;
9594 // ============================================================================
9595 // setup file functions
9596 // ============================================================================
9598 #define TOKEN_STR_PLAYER_PREFIX "player_"
9601 static struct TokenInfo global_setup_tokens[] =
9605 &setup.player_name, "player_name"
9609 &setup.multiple_users, "multiple_users"
9613 &setup.sound, "sound"
9617 &setup.sound_loops, "repeating_sound_loops"
9621 &setup.sound_music, "background_music"
9625 &setup.sound_simple, "simple_sound_effects"
9629 &setup.toons, "toons"
9633 &setup.global_animations, "global_animations"
9637 &setup.scroll_delay, "scroll_delay"
9641 &setup.forced_scroll_delay, "forced_scroll_delay"
9645 &setup.scroll_delay_value, "scroll_delay_value"
9649 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9653 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9657 &setup.fade_screens, "fade_screens"
9661 &setup.autorecord, "automatic_tape_recording"
9665 &setup.autorecord_after_replay, "autorecord_after_replay"
9669 &setup.auto_pause_on_start, "auto_pause_on_start"
9673 &setup.show_titlescreen, "show_titlescreen"
9677 &setup.quick_doors, "quick_doors"
9681 &setup.team_mode, "team_mode"
9685 &setup.handicap, "handicap"
9689 &setup.skip_levels, "skip_levels"
9693 &setup.increment_levels, "increment_levels"
9697 &setup.auto_play_next_level, "auto_play_next_level"
9701 &setup.count_score_after_game, "count_score_after_game"
9705 &setup.show_scores_after_game, "show_scores_after_game"
9709 &setup.time_limit, "time_limit"
9713 &setup.fullscreen, "fullscreen"
9717 &setup.window_scaling_percent, "window_scaling_percent"
9721 &setup.window_scaling_quality, "window_scaling_quality"
9725 &setup.screen_rendering_mode, "screen_rendering_mode"
9729 &setup.vsync_mode, "vsync_mode"
9733 &setup.ask_on_escape, "ask_on_escape"
9737 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9741 &setup.ask_on_game_over, "ask_on_game_over"
9745 &setup.ask_on_quit_game, "ask_on_quit_game"
9749 &setup.ask_on_quit_program, "ask_on_quit_program"
9753 &setup.quick_switch, "quick_player_switch"
9757 &setup.input_on_focus, "input_on_focus"
9761 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9765 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9769 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9773 &setup.game_speed_extended, "game_speed_extended"
9777 &setup.game_frame_delay, "game_frame_delay"
9781 &setup.sp_show_border_elements, "sp_show_border_elements"
9785 &setup.small_game_graphics, "small_game_graphics"
9789 &setup.show_load_save_buttons, "show_load_save_buttons"
9793 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9797 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9801 &setup.graphics_set, "graphics_set"
9805 &setup.sounds_set, "sounds_set"
9809 &setup.music_set, "music_set"
9813 &setup.override_level_graphics, "override_level_graphics"
9817 &setup.override_level_sounds, "override_level_sounds"
9821 &setup.override_level_music, "override_level_music"
9825 &setup.volume_simple, "volume_simple"
9829 &setup.volume_loops, "volume_loops"
9833 &setup.volume_music, "volume_music"
9837 &setup.network_mode, "network_mode"
9841 &setup.network_player_nr, "network_player"
9845 &setup.network_server_hostname, "network_server_hostname"
9849 &setup.touch.control_type, "touch.control_type"
9853 &setup.touch.move_distance, "touch.move_distance"
9857 &setup.touch.drop_distance, "touch.drop_distance"
9861 &setup.touch.transparency, "touch.transparency"
9865 &setup.touch.draw_outlined, "touch.draw_outlined"
9869 &setup.touch.draw_pressed, "touch.draw_pressed"
9873 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9877 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9881 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9885 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9889 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9893 static struct TokenInfo auto_setup_tokens[] =
9897 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9901 static struct TokenInfo server_setup_tokens[] =
9905 &setup.player_uuid, "player_uuid"
9909 &setup.player_version, "player_version"
9913 &setup.use_api_server, TEST_PREFIX "use_api_server"
9917 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9921 &setup.api_server_password, TEST_PREFIX "api_server_password"
9925 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9929 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9933 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9937 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9941 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9945 static struct TokenInfo editor_setup_tokens[] =
9949 &setup.editor.el_classic, "editor.el_classic"
9953 &setup.editor.el_custom, "editor.el_custom"
9957 &setup.editor.el_user_defined, "editor.el_user_defined"
9961 &setup.editor.el_dynamic, "editor.el_dynamic"
9965 &setup.editor.el_headlines, "editor.el_headlines"
9969 &setup.editor.show_element_token, "editor.show_element_token"
9973 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9977 static struct TokenInfo editor_cascade_setup_tokens[] =
9981 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9985 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
9989 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9993 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9997 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10001 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10005 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10009 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10013 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10017 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10021 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10025 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10029 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10033 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10037 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10041 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10045 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10049 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10053 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10057 static struct TokenInfo shortcut_setup_tokens[] =
10061 &setup.shortcut.save_game, "shortcut.save_game"
10065 &setup.shortcut.load_game, "shortcut.load_game"
10069 &setup.shortcut.restart_game, "shortcut.restart_game"
10073 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10077 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10081 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10085 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10089 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10093 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10097 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10101 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10105 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10109 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10113 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10117 &setup.shortcut.tape_record, "shortcut.tape_record"
10121 &setup.shortcut.tape_play, "shortcut.tape_play"
10125 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10129 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10133 &setup.shortcut.sound_music, "shortcut.sound_music"
10137 &setup.shortcut.snap_left, "shortcut.snap_left"
10141 &setup.shortcut.snap_right, "shortcut.snap_right"
10145 &setup.shortcut.snap_up, "shortcut.snap_up"
10149 &setup.shortcut.snap_down, "shortcut.snap_down"
10153 static struct SetupInputInfo setup_input;
10154 static struct TokenInfo player_setup_tokens[] =
10158 &setup_input.use_joystick, ".use_joystick"
10162 &setup_input.joy.device_name, ".joy.device_name"
10166 &setup_input.joy.xleft, ".joy.xleft"
10170 &setup_input.joy.xmiddle, ".joy.xmiddle"
10174 &setup_input.joy.xright, ".joy.xright"
10178 &setup_input.joy.yupper, ".joy.yupper"
10182 &setup_input.joy.ymiddle, ".joy.ymiddle"
10186 &setup_input.joy.ylower, ".joy.ylower"
10190 &setup_input.joy.snap, ".joy.snap_field"
10194 &setup_input.joy.drop, ".joy.place_bomb"
10198 &setup_input.key.left, ".key.move_left"
10202 &setup_input.key.right, ".key.move_right"
10206 &setup_input.key.up, ".key.move_up"
10210 &setup_input.key.down, ".key.move_down"
10214 &setup_input.key.snap, ".key.snap_field"
10218 &setup_input.key.drop, ".key.place_bomb"
10222 static struct TokenInfo system_setup_tokens[] =
10226 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10230 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10234 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10238 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10242 static struct TokenInfo internal_setup_tokens[] =
10246 &setup.internal.program_title, "program_title"
10250 &setup.internal.program_version, "program_version"
10254 &setup.internal.program_author, "program_author"
10258 &setup.internal.program_email, "program_email"
10262 &setup.internal.program_website, "program_website"
10266 &setup.internal.program_copyright, "program_copyright"
10270 &setup.internal.program_company, "program_company"
10274 &setup.internal.program_icon_file, "program_icon_file"
10278 &setup.internal.default_graphics_set, "default_graphics_set"
10282 &setup.internal.default_sounds_set, "default_sounds_set"
10286 &setup.internal.default_music_set, "default_music_set"
10290 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10294 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10298 &setup.internal.fallback_music_file, "fallback_music_file"
10302 &setup.internal.default_level_series, "default_level_series"
10306 &setup.internal.default_window_width, "default_window_width"
10310 &setup.internal.default_window_height, "default_window_height"
10314 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10318 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10322 &setup.internal.create_user_levelset, "create_user_levelset"
10326 &setup.internal.info_screens_from_main, "info_screens_from_main"
10330 &setup.internal.menu_game, "menu_game"
10334 &setup.internal.menu_engines, "menu_engines"
10338 &setup.internal.menu_editor, "menu_editor"
10342 &setup.internal.menu_graphics, "menu_graphics"
10346 &setup.internal.menu_sound, "menu_sound"
10350 &setup.internal.menu_artwork, "menu_artwork"
10354 &setup.internal.menu_input, "menu_input"
10358 &setup.internal.menu_touch, "menu_touch"
10362 &setup.internal.menu_shortcuts, "menu_shortcuts"
10366 &setup.internal.menu_exit, "menu_exit"
10370 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10374 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10378 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10382 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10386 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10390 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10394 &setup.internal.info_title, "info_title"
10398 &setup.internal.info_elements, "info_elements"
10402 &setup.internal.info_music, "info_music"
10406 &setup.internal.info_credits, "info_credits"
10410 &setup.internal.info_program, "info_program"
10414 &setup.internal.info_version, "info_version"
10418 &setup.internal.info_levelset, "info_levelset"
10422 &setup.internal.info_exit, "info_exit"
10426 static struct TokenInfo debug_setup_tokens[] =
10430 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10434 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10438 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10442 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10446 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10450 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10454 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10458 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10462 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10466 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10470 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10474 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10478 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10482 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10486 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10490 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10494 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10498 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10502 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10506 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10510 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10513 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10517 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10521 &setup.debug.xsn_mode, "debug.xsn_mode"
10525 &setup.debug.xsn_percent, "debug.xsn_percent"
10529 static struct TokenInfo options_setup_tokens[] =
10533 &setup.options.verbose, "options.verbose"
10537 &setup.options.debug, "options.debug"
10541 &setup.options.debug_mode, "options.debug_mode"
10545 static void setSetupInfoToDefaults(struct SetupInfo *si)
10549 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10551 si->multiple_users = TRUE;
10554 si->sound_loops = TRUE;
10555 si->sound_music = TRUE;
10556 si->sound_simple = TRUE;
10558 si->global_animations = TRUE;
10559 si->scroll_delay = TRUE;
10560 si->forced_scroll_delay = FALSE;
10561 si->scroll_delay_value = STD_SCROLL_DELAY;
10562 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10563 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10564 si->fade_screens = TRUE;
10565 si->autorecord = TRUE;
10566 si->autorecord_after_replay = TRUE;
10567 si->auto_pause_on_start = FALSE;
10568 si->show_titlescreen = TRUE;
10569 si->quick_doors = FALSE;
10570 si->team_mode = FALSE;
10571 si->handicap = TRUE;
10572 si->skip_levels = TRUE;
10573 si->increment_levels = TRUE;
10574 si->auto_play_next_level = TRUE;
10575 si->count_score_after_game = TRUE;
10576 si->show_scores_after_game = TRUE;
10577 si->time_limit = TRUE;
10578 si->fullscreen = FALSE;
10579 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10580 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10581 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10582 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10583 si->ask_on_escape = TRUE;
10584 si->ask_on_escape_editor = TRUE;
10585 si->ask_on_game_over = TRUE;
10586 si->ask_on_quit_game = TRUE;
10587 si->ask_on_quit_program = TRUE;
10588 si->quick_switch = FALSE;
10589 si->input_on_focus = FALSE;
10590 si->prefer_aga_graphics = TRUE;
10591 si->prefer_lowpass_sounds = FALSE;
10592 si->prefer_extra_panel_items = TRUE;
10593 si->game_speed_extended = FALSE;
10594 si->game_frame_delay = GAME_FRAME_DELAY;
10595 si->sp_show_border_elements = FALSE;
10596 si->small_game_graphics = FALSE;
10597 si->show_load_save_buttons = FALSE;
10598 si->show_undo_redo_buttons = FALSE;
10599 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10601 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10602 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10603 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10605 si->override_level_graphics = FALSE;
10606 si->override_level_sounds = FALSE;
10607 si->override_level_music = FALSE;
10609 si->volume_simple = 100; // percent
10610 si->volume_loops = 100; // percent
10611 si->volume_music = 100; // percent
10613 si->network_mode = FALSE;
10614 si->network_player_nr = 0; // first player
10615 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10617 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10618 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10619 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10620 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10621 si->touch.draw_outlined = TRUE;
10622 si->touch.draw_pressed = TRUE;
10624 for (i = 0; i < 2; i++)
10626 char *default_grid_button[6][2] =
10632 { "111222", " vv " },
10633 { "111222", " vv " }
10635 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10636 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10637 int min_xsize = MIN(6, grid_xsize);
10638 int min_ysize = MIN(6, grid_ysize);
10639 int startx = grid_xsize - min_xsize;
10640 int starty = grid_ysize - min_ysize;
10643 // virtual buttons grid can only be set to defaults if video is initialized
10644 // (this will be repeated if virtual buttons are not loaded from setup file)
10645 if (video.initialized)
10647 si->touch.grid_xsize[i] = grid_xsize;
10648 si->touch.grid_ysize[i] = grid_ysize;
10652 si->touch.grid_xsize[i] = -1;
10653 si->touch.grid_ysize[i] = -1;
10656 for (x = 0; x < MAX_GRID_XSIZE; x++)
10657 for (y = 0; y < MAX_GRID_YSIZE; y++)
10658 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10660 for (x = 0; x < min_xsize; x++)
10661 for (y = 0; y < min_ysize; y++)
10662 si->touch.grid_button[i][x][starty + y] =
10663 default_grid_button[y][0][x];
10665 for (x = 0; x < min_xsize; x++)
10666 for (y = 0; y < min_ysize; y++)
10667 si->touch.grid_button[i][startx + x][starty + y] =
10668 default_grid_button[y][1][x];
10671 si->touch.grid_initialized = video.initialized;
10673 si->touch.overlay_buttons = FALSE;
10675 si->editor.el_boulderdash = TRUE;
10676 si->editor.el_boulderdash_native = TRUE;
10677 si->editor.el_emerald_mine = TRUE;
10678 si->editor.el_emerald_mine_club = TRUE;
10679 si->editor.el_more = TRUE;
10680 si->editor.el_sokoban = TRUE;
10681 si->editor.el_supaplex = TRUE;
10682 si->editor.el_diamond_caves = TRUE;
10683 si->editor.el_dx_boulderdash = TRUE;
10685 si->editor.el_mirror_magic = TRUE;
10686 si->editor.el_deflektor = TRUE;
10688 si->editor.el_chars = TRUE;
10689 si->editor.el_steel_chars = TRUE;
10691 si->editor.el_classic = TRUE;
10692 si->editor.el_custom = TRUE;
10694 si->editor.el_user_defined = FALSE;
10695 si->editor.el_dynamic = TRUE;
10697 si->editor.el_headlines = TRUE;
10699 si->editor.show_element_token = FALSE;
10701 si->editor.show_read_only_warning = TRUE;
10703 si->editor.use_template_for_new_levels = TRUE;
10705 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10706 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10707 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10708 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10709 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10711 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10712 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10713 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10714 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10715 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10717 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10718 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10719 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10720 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10721 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10722 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10724 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10725 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10726 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10728 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10729 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10730 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10731 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10733 for (i = 0; i < MAX_PLAYERS; i++)
10735 si->input[i].use_joystick = FALSE;
10736 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10737 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10738 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10739 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10740 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10741 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10742 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10743 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10744 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10745 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10746 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10747 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10748 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10749 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10750 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10753 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10754 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10755 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10756 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10758 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10759 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10760 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10761 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10762 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10763 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10764 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10766 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10768 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10769 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10770 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10772 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10773 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10774 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10776 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10777 si->internal.choose_from_top_leveldir = FALSE;
10778 si->internal.show_scaling_in_title = TRUE;
10779 si->internal.create_user_levelset = TRUE;
10780 si->internal.info_screens_from_main = FALSE;
10782 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10783 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10785 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10786 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10787 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10788 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10789 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10790 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10791 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10792 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10793 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10794 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10796 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10797 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10798 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10799 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10800 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10801 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10802 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10803 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10804 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10805 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10807 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10808 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10810 si->debug.show_frames_per_second = FALSE;
10812 si->debug.xsn_mode = AUTO;
10813 si->debug.xsn_percent = 0;
10815 si->options.verbose = FALSE;
10816 si->options.debug = FALSE;
10817 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10819 #if defined(PLATFORM_ANDROID)
10820 si->fullscreen = TRUE;
10821 si->touch.overlay_buttons = TRUE;
10824 setHideSetupEntry(&setup.debug.xsn_mode);
10827 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10829 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10832 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10834 si->player_uuid = NULL; // (will be set later)
10835 si->player_version = 1; // (will be set later)
10837 si->use_api_server = TRUE;
10838 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10839 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10840 si->ask_for_uploading_tapes = TRUE;
10841 si->ask_for_remaining_tapes = FALSE;
10842 si->provide_uploading_tapes = TRUE;
10843 si->ask_for_using_api_server = TRUE;
10844 si->has_remaining_tapes = FALSE;
10847 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10849 si->editor_cascade.el_bd = TRUE;
10850 si->editor_cascade.el_bd_native = TRUE;
10851 si->editor_cascade.el_em = TRUE;
10852 si->editor_cascade.el_emc = TRUE;
10853 si->editor_cascade.el_rnd = TRUE;
10854 si->editor_cascade.el_sb = TRUE;
10855 si->editor_cascade.el_sp = TRUE;
10856 si->editor_cascade.el_dc = TRUE;
10857 si->editor_cascade.el_dx = TRUE;
10859 si->editor_cascade.el_mm = TRUE;
10860 si->editor_cascade.el_df = TRUE;
10862 si->editor_cascade.el_chars = FALSE;
10863 si->editor_cascade.el_steel_chars = FALSE;
10864 si->editor_cascade.el_ce = FALSE;
10865 si->editor_cascade.el_ge = FALSE;
10866 si->editor_cascade.el_es = FALSE;
10867 si->editor_cascade.el_ref = FALSE;
10868 si->editor_cascade.el_user = FALSE;
10869 si->editor_cascade.el_dynamic = FALSE;
10872 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10874 static char *getHideSetupToken(void *setup_value)
10876 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10878 if (setup_value != NULL)
10879 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10881 return hide_setup_token;
10884 void setHideSetupEntry(void *setup_value)
10886 char *hide_setup_token = getHideSetupToken(setup_value);
10888 if (hide_setup_hash == NULL)
10889 hide_setup_hash = newSetupFileHash();
10891 if (setup_value != NULL)
10892 setHashEntry(hide_setup_hash, hide_setup_token, "");
10895 void removeHideSetupEntry(void *setup_value)
10897 char *hide_setup_token = getHideSetupToken(setup_value);
10899 if (setup_value != NULL)
10900 removeHashEntry(hide_setup_hash, hide_setup_token);
10903 boolean hideSetupEntry(void *setup_value)
10905 char *hide_setup_token = getHideSetupToken(setup_value);
10907 return (setup_value != NULL &&
10908 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10911 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10912 struct TokenInfo *token_info,
10913 int token_nr, char *token_text)
10915 char *token_hide_text = getStringCat2(token_text, ".hide");
10916 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10918 // set the value of this setup option in the setup option structure
10919 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10921 // check if this setup option should be hidden in the setup menu
10922 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10923 setHideSetupEntry(token_info[token_nr].value);
10925 free(token_hide_text);
10928 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10929 struct TokenInfo *token_info,
10932 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10933 token_info[token_nr].text);
10936 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10940 if (!setup_file_hash)
10943 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10944 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10946 setup.touch.grid_initialized = TRUE;
10947 for (i = 0; i < 2; i++)
10949 int grid_xsize = setup.touch.grid_xsize[i];
10950 int grid_ysize = setup.touch.grid_ysize[i];
10953 // if virtual buttons are not loaded from setup file, repeat initializing
10954 // virtual buttons grid with default values later when video is initialized
10955 if (grid_xsize == -1 ||
10958 setup.touch.grid_initialized = FALSE;
10963 for (y = 0; y < grid_ysize; y++)
10965 char token_string[MAX_LINE_LEN];
10967 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10969 char *value_string = getHashEntry(setup_file_hash, token_string);
10971 if (value_string == NULL)
10974 for (x = 0; x < grid_xsize; x++)
10976 char c = value_string[x];
10978 setup.touch.grid_button[i][x][y] =
10979 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10984 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10985 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10987 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10988 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10990 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10994 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10996 setup_input = setup.input[pnr];
10997 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10999 char full_token[100];
11001 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11002 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11005 setup.input[pnr] = setup_input;
11008 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11009 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11011 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11012 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11014 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11015 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11017 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11018 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11020 setHideRelatedSetupEntries();
11023 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11027 if (!setup_file_hash)
11030 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11031 setSetupInfo(auto_setup_tokens, i,
11032 getHashEntry(setup_file_hash,
11033 auto_setup_tokens[i].text));
11036 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11040 if (!setup_file_hash)
11043 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11044 setSetupInfo(server_setup_tokens, i,
11045 getHashEntry(setup_file_hash,
11046 server_setup_tokens[i].text));
11049 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11053 if (!setup_file_hash)
11056 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11057 setSetupInfo(editor_cascade_setup_tokens, i,
11058 getHashEntry(setup_file_hash,
11059 editor_cascade_setup_tokens[i].text));
11062 void LoadUserNames(void)
11064 int last_user_nr = user.nr;
11067 if (global.user_names != NULL)
11069 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11070 checked_free(global.user_names[i]);
11072 checked_free(global.user_names);
11075 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11077 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11081 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11083 if (setup_file_hash)
11085 char *player_name = getHashEntry(setup_file_hash, "player_name");
11087 global.user_names[i] = getFixedUserName(player_name);
11089 freeSetupFileHash(setup_file_hash);
11092 if (global.user_names[i] == NULL)
11093 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11096 user.nr = last_user_nr;
11099 void LoadSetupFromFilename(char *filename)
11101 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11103 if (setup_file_hash)
11105 decodeSetupFileHash_Default(setup_file_hash);
11107 freeSetupFileHash(setup_file_hash);
11111 Debug("setup", "using default setup values");
11115 static void LoadSetup_SpecialPostProcessing(void)
11117 char *player_name_new;
11119 // needed to work around problems with fixed length strings
11120 player_name_new = getFixedUserName(setup.player_name);
11121 free(setup.player_name);
11122 setup.player_name = player_name_new;
11124 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11125 if (setup.scroll_delay == FALSE)
11127 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11128 setup.scroll_delay = TRUE; // now always "on"
11131 // make sure that scroll delay value stays inside valid range
11132 setup.scroll_delay_value =
11133 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11136 void LoadSetup_Default(void)
11140 // always start with reliable default values
11141 setSetupInfoToDefaults(&setup);
11143 // try to load setup values from default setup file
11144 filename = getDefaultSetupFilename();
11146 if (fileExists(filename))
11147 LoadSetupFromFilename(filename);
11149 // try to load setup values from platform setup file
11150 filename = getPlatformSetupFilename();
11152 if (fileExists(filename))
11153 LoadSetupFromFilename(filename);
11155 // try to load setup values from user setup file
11156 filename = getSetupFilename();
11158 LoadSetupFromFilename(filename);
11160 LoadSetup_SpecialPostProcessing();
11163 void LoadSetup_AutoSetup(void)
11165 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11166 SetupFileHash *setup_file_hash = NULL;
11168 // always start with reliable default values
11169 setSetupInfoToDefaults_AutoSetup(&setup);
11171 setup_file_hash = loadSetupFileHash(filename);
11173 if (setup_file_hash)
11175 decodeSetupFileHash_AutoSetup(setup_file_hash);
11177 freeSetupFileHash(setup_file_hash);
11183 void LoadSetup_ServerSetup(void)
11185 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11186 SetupFileHash *setup_file_hash = NULL;
11188 // always start with reliable default values
11189 setSetupInfoToDefaults_ServerSetup(&setup);
11191 setup_file_hash = loadSetupFileHash(filename);
11193 if (setup_file_hash)
11195 decodeSetupFileHash_ServerSetup(setup_file_hash);
11197 freeSetupFileHash(setup_file_hash);
11202 if (setup.player_uuid == NULL)
11204 // player UUID does not yet exist in setup file
11205 setup.player_uuid = getStringCopy(getUUID());
11206 setup.player_version = 2;
11208 SaveSetup_ServerSetup();
11212 void LoadSetup_EditorCascade(void)
11214 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11215 SetupFileHash *setup_file_hash = NULL;
11217 // always start with reliable default values
11218 setSetupInfoToDefaults_EditorCascade(&setup);
11220 setup_file_hash = loadSetupFileHash(filename);
11222 if (setup_file_hash)
11224 decodeSetupFileHash_EditorCascade(setup_file_hash);
11226 freeSetupFileHash(setup_file_hash);
11232 void LoadSetup(void)
11234 LoadSetup_Default();
11235 LoadSetup_AutoSetup();
11236 LoadSetup_ServerSetup();
11237 LoadSetup_EditorCascade();
11240 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11241 char *mapping_line)
11243 char mapping_guid[MAX_LINE_LEN];
11244 char *mapping_start, *mapping_end;
11246 // get GUID from game controller mapping line: copy complete line
11247 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11248 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11250 // get GUID from game controller mapping line: cut after GUID part
11251 mapping_start = strchr(mapping_guid, ',');
11252 if (mapping_start != NULL)
11253 *mapping_start = '\0';
11255 // cut newline from game controller mapping line
11256 mapping_end = strchr(mapping_line, '\n');
11257 if (mapping_end != NULL)
11258 *mapping_end = '\0';
11260 // add mapping entry to game controller mappings hash
11261 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11264 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11269 if (!(file = fopen(filename, MODE_READ)))
11271 Warn("cannot read game controller mappings file '%s'", filename);
11276 while (!feof(file))
11278 char line[MAX_LINE_LEN];
11280 if (!fgets(line, MAX_LINE_LEN, file))
11283 addGameControllerMappingToHash(mappings_hash, line);
11289 void SaveSetup_Default(void)
11291 char *filename = getSetupFilename();
11295 InitUserDataDirectory();
11297 if (!(file = fopen(filename, MODE_WRITE)))
11299 Warn("cannot write setup file '%s'", filename);
11304 fprintFileHeader(file, SETUP_FILENAME);
11306 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11308 // just to make things nicer :)
11309 if (global_setup_tokens[i].value == &setup.multiple_users ||
11310 global_setup_tokens[i].value == &setup.sound ||
11311 global_setup_tokens[i].value == &setup.graphics_set ||
11312 global_setup_tokens[i].value == &setup.volume_simple ||
11313 global_setup_tokens[i].value == &setup.network_mode ||
11314 global_setup_tokens[i].value == &setup.touch.control_type ||
11315 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11316 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11317 fprintf(file, "\n");
11319 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11322 for (i = 0; i < 2; i++)
11324 int grid_xsize = setup.touch.grid_xsize[i];
11325 int grid_ysize = setup.touch.grid_ysize[i];
11328 fprintf(file, "\n");
11330 for (y = 0; y < grid_ysize; y++)
11332 char token_string[MAX_LINE_LEN];
11333 char value_string[MAX_LINE_LEN];
11335 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11337 for (x = 0; x < grid_xsize; x++)
11339 char c = setup.touch.grid_button[i][x][y];
11341 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11344 value_string[grid_xsize] = '\0';
11346 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11350 fprintf(file, "\n");
11351 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11352 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11354 fprintf(file, "\n");
11355 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11356 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11358 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11362 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11363 fprintf(file, "\n");
11365 setup_input = setup.input[pnr];
11366 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11367 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11370 fprintf(file, "\n");
11371 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11372 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11374 // (internal setup values not saved to user setup file)
11376 fprintf(file, "\n");
11377 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11378 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11379 setup.debug.xsn_mode != AUTO)
11380 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11382 fprintf(file, "\n");
11383 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11384 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11388 SetFilePermissions(filename, PERMS_PRIVATE);
11391 void SaveSetup_AutoSetup(void)
11393 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11397 InitUserDataDirectory();
11399 if (!(file = fopen(filename, MODE_WRITE)))
11401 Warn("cannot write auto setup file '%s'", filename);
11408 fprintFileHeader(file, AUTOSETUP_FILENAME);
11410 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11411 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11415 SetFilePermissions(filename, PERMS_PRIVATE);
11420 void SaveSetup_ServerSetup(void)
11422 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11426 InitUserDataDirectory();
11428 if (!(file = fopen(filename, MODE_WRITE)))
11430 Warn("cannot write server setup file '%s'", filename);
11437 fprintFileHeader(file, SERVERSETUP_FILENAME);
11439 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11441 // just to make things nicer :)
11442 if (server_setup_tokens[i].value == &setup.use_api_server)
11443 fprintf(file, "\n");
11445 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11450 SetFilePermissions(filename, PERMS_PRIVATE);
11455 void SaveSetup_EditorCascade(void)
11457 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11461 InitUserDataDirectory();
11463 if (!(file = fopen(filename, MODE_WRITE)))
11465 Warn("cannot write editor cascade state file '%s'", filename);
11472 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11474 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11475 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11479 SetFilePermissions(filename, PERMS_PRIVATE);
11484 void SaveSetup(void)
11486 SaveSetup_Default();
11487 SaveSetup_AutoSetup();
11488 SaveSetup_ServerSetup();
11489 SaveSetup_EditorCascade();
11492 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11497 if (!(file = fopen(filename, MODE_WRITE)))
11499 Warn("cannot write game controller mappings file '%s'", filename);
11504 BEGIN_HASH_ITERATION(mappings_hash, itr)
11506 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11508 END_HASH_ITERATION(mappings_hash, itr)
11513 void SaveSetup_AddGameControllerMapping(char *mapping)
11515 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11516 SetupFileHash *mappings_hash = newSetupFileHash();
11518 InitUserDataDirectory();
11520 // load existing personal game controller mappings
11521 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11523 // add new mapping to personal game controller mappings
11524 addGameControllerMappingToHash(mappings_hash, mapping);
11526 // save updated personal game controller mappings
11527 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11529 freeSetupFileHash(mappings_hash);
11533 void LoadCustomElementDescriptions(void)
11535 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11536 SetupFileHash *setup_file_hash;
11539 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11541 if (element_info[i].custom_description != NULL)
11543 free(element_info[i].custom_description);
11544 element_info[i].custom_description = NULL;
11548 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11551 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11553 char *token = getStringCat2(element_info[i].token_name, ".name");
11554 char *value = getHashEntry(setup_file_hash, token);
11557 element_info[i].custom_description = getStringCopy(value);
11562 freeSetupFileHash(setup_file_hash);
11565 static int getElementFromToken(char *token)
11567 char *value = getHashEntry(element_token_hash, token);
11570 return atoi(value);
11572 Warn("unknown element token '%s'", token);
11574 return EL_UNDEFINED;
11577 void FreeGlobalAnimEventInfo(void)
11579 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11581 if (gaei->event_list == NULL)
11586 for (i = 0; i < gaei->num_event_lists; i++)
11588 checked_free(gaei->event_list[i]->event_value);
11589 checked_free(gaei->event_list[i]);
11592 checked_free(gaei->event_list);
11594 gaei->event_list = NULL;
11595 gaei->num_event_lists = 0;
11598 static int AddGlobalAnimEventList(void)
11600 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11601 int list_pos = gaei->num_event_lists++;
11603 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11604 sizeof(struct GlobalAnimEventListInfo *));
11606 gaei->event_list[list_pos] =
11607 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11609 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11611 gaeli->event_value = NULL;
11612 gaeli->num_event_values = 0;
11617 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11619 // do not add empty global animation events
11620 if (event_value == ANIM_EVENT_NONE)
11623 // if list position is undefined, create new list
11624 if (list_pos == ANIM_EVENT_UNDEFINED)
11625 list_pos = AddGlobalAnimEventList();
11627 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11628 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11629 int value_pos = gaeli->num_event_values++;
11631 gaeli->event_value = checked_realloc(gaeli->event_value,
11632 gaeli->num_event_values * sizeof(int *));
11634 gaeli->event_value[value_pos] = event_value;
11639 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11641 if (list_pos == ANIM_EVENT_UNDEFINED)
11642 return ANIM_EVENT_NONE;
11644 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11645 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11647 return gaeli->event_value[value_pos];
11650 int GetGlobalAnimEventValueCount(int list_pos)
11652 if (list_pos == ANIM_EVENT_UNDEFINED)
11655 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11656 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11658 return gaeli->num_event_values;
11661 // This function checks if a string <s> of the format "string1, string2, ..."
11662 // exactly contains a string <s_contained>.
11664 static boolean string_has_parameter(char *s, char *s_contained)
11668 if (s == NULL || s_contained == NULL)
11671 if (strlen(s_contained) > strlen(s))
11674 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11676 char next_char = s[strlen(s_contained)];
11678 // check if next character is delimiter or whitespace
11679 if (next_char == ',' || next_char == '\0' ||
11680 next_char == ' ' || next_char == '\t')
11684 // check if string contains another parameter string after a comma
11685 substring = strchr(s, ',');
11686 if (substring == NULL) // string does not contain a comma
11689 // advance string pointer to next character after the comma
11692 // skip potential whitespaces after the comma
11693 while (*substring == ' ' || *substring == '\t')
11696 return string_has_parameter(substring, s_contained);
11699 static int get_anim_parameter_value_ce(char *s)
11702 char *pattern_1 = "ce_change:custom_";
11703 char *pattern_2 = ".page_";
11704 int pattern_1_len = strlen(pattern_1);
11705 char *matching_char = strstr(s_ptr, pattern_1);
11706 int result = ANIM_EVENT_NONE;
11708 if (matching_char == NULL)
11709 return ANIM_EVENT_NONE;
11711 result = ANIM_EVENT_CE_CHANGE;
11713 s_ptr = matching_char + pattern_1_len;
11715 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11716 if (*s_ptr >= '0' && *s_ptr <= '9')
11718 int gic_ce_nr = (*s_ptr++ - '0');
11720 if (*s_ptr >= '0' && *s_ptr <= '9')
11722 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11724 if (*s_ptr >= '0' && *s_ptr <= '9')
11725 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11728 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11729 return ANIM_EVENT_NONE;
11731 // custom element stored as 0 to 255
11734 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11738 // invalid custom element number specified
11740 return ANIM_EVENT_NONE;
11743 // check for change page number ("page_X" or "page_XX") (optional)
11744 if (strPrefix(s_ptr, pattern_2))
11746 s_ptr += strlen(pattern_2);
11748 if (*s_ptr >= '0' && *s_ptr <= '9')
11750 int gic_page_nr = (*s_ptr++ - '0');
11752 if (*s_ptr >= '0' && *s_ptr <= '9')
11753 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11755 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11756 return ANIM_EVENT_NONE;
11758 // change page stored as 1 to 32 (0 means "all change pages")
11760 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11764 // invalid animation part number specified
11766 return ANIM_EVENT_NONE;
11770 // discard result if next character is neither delimiter nor whitespace
11771 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11772 *s_ptr == ' ' || *s_ptr == '\t'))
11773 return ANIM_EVENT_NONE;
11778 static int get_anim_parameter_value(char *s)
11780 int event_value[] =
11788 char *pattern_1[] =
11796 char *pattern_2 = ".part_";
11797 char *matching_char = NULL;
11799 int pattern_1_len = 0;
11800 int result = ANIM_EVENT_NONE;
11803 result = get_anim_parameter_value_ce(s);
11805 if (result != ANIM_EVENT_NONE)
11808 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11810 matching_char = strstr(s_ptr, pattern_1[i]);
11811 pattern_1_len = strlen(pattern_1[i]);
11812 result = event_value[i];
11814 if (matching_char != NULL)
11818 if (matching_char == NULL)
11819 return ANIM_EVENT_NONE;
11821 s_ptr = matching_char + pattern_1_len;
11823 // check for main animation number ("anim_X" or "anim_XX")
11824 if (*s_ptr >= '0' && *s_ptr <= '9')
11826 int gic_anim_nr = (*s_ptr++ - '0');
11828 if (*s_ptr >= '0' && *s_ptr <= '9')
11829 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11831 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11832 return ANIM_EVENT_NONE;
11834 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11838 // invalid main animation number specified
11840 return ANIM_EVENT_NONE;
11843 // check for animation part number ("part_X" or "part_XX") (optional)
11844 if (strPrefix(s_ptr, pattern_2))
11846 s_ptr += strlen(pattern_2);
11848 if (*s_ptr >= '0' && *s_ptr <= '9')
11850 int gic_part_nr = (*s_ptr++ - '0');
11852 if (*s_ptr >= '0' && *s_ptr <= '9')
11853 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11855 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11856 return ANIM_EVENT_NONE;
11858 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11862 // invalid animation part number specified
11864 return ANIM_EVENT_NONE;
11868 // discard result if next character is neither delimiter nor whitespace
11869 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11870 *s_ptr == ' ' || *s_ptr == '\t'))
11871 return ANIM_EVENT_NONE;
11876 static int get_anim_parameter_values(char *s)
11878 int list_pos = ANIM_EVENT_UNDEFINED;
11879 int event_value = ANIM_EVENT_DEFAULT;
11881 if (string_has_parameter(s, "any"))
11882 event_value |= ANIM_EVENT_ANY;
11884 if (string_has_parameter(s, "click:self") ||
11885 string_has_parameter(s, "click") ||
11886 string_has_parameter(s, "self"))
11887 event_value |= ANIM_EVENT_SELF;
11889 if (string_has_parameter(s, "unclick:any"))
11890 event_value |= ANIM_EVENT_UNCLICK_ANY;
11892 // if animation event found, add it to global animation event list
11893 if (event_value != ANIM_EVENT_NONE)
11894 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11898 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11899 event_value = get_anim_parameter_value(s);
11901 // if animation event found, add it to global animation event list
11902 if (event_value != ANIM_EVENT_NONE)
11903 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11905 // continue with next part of the string, starting with next comma
11906 s = strchr(s + 1, ',');
11912 static int get_anim_action_parameter_value(char *token)
11914 // check most common default case first to massively speed things up
11915 if (strEqual(token, ARG_UNDEFINED))
11916 return ANIM_EVENT_ACTION_NONE;
11918 int result = getImageIDFromToken(token);
11922 char *gfx_token = getStringCat2("gfx.", token);
11924 result = getImageIDFromToken(gfx_token);
11926 checked_free(gfx_token);
11931 Key key = getKeyFromX11KeyName(token);
11933 if (key != KSYM_UNDEFINED)
11934 result = -(int)key;
11941 result = get_hash_from_key(token); // unsigned int => int
11942 result = ABS(result); // may be negative now
11943 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11945 setHashEntry(anim_url_hash, int2str(result, 0), token);
11950 result = ANIM_EVENT_ACTION_NONE;
11955 int get_parameter_value(char *value_raw, char *suffix, int type)
11957 char *value = getStringToLower(value_raw);
11958 int result = 0; // probably a save default value
11960 if (strEqual(suffix, ".direction"))
11962 result = (strEqual(value, "left") ? MV_LEFT :
11963 strEqual(value, "right") ? MV_RIGHT :
11964 strEqual(value, "up") ? MV_UP :
11965 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11967 else if (strEqual(suffix, ".position"))
11969 result = (strEqual(value, "left") ? POS_LEFT :
11970 strEqual(value, "right") ? POS_RIGHT :
11971 strEqual(value, "top") ? POS_TOP :
11972 strEqual(value, "upper") ? POS_UPPER :
11973 strEqual(value, "middle") ? POS_MIDDLE :
11974 strEqual(value, "lower") ? POS_LOWER :
11975 strEqual(value, "bottom") ? POS_BOTTOM :
11976 strEqual(value, "any") ? POS_ANY :
11977 strEqual(value, "ce") ? POS_CE :
11978 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
11979 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11981 else if (strEqual(suffix, ".align"))
11983 result = (strEqual(value, "left") ? ALIGN_LEFT :
11984 strEqual(value, "right") ? ALIGN_RIGHT :
11985 strEqual(value, "center") ? ALIGN_CENTER :
11986 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11988 else if (strEqual(suffix, ".valign"))
11990 result = (strEqual(value, "top") ? VALIGN_TOP :
11991 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11992 strEqual(value, "middle") ? VALIGN_MIDDLE :
11993 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11995 else if (strEqual(suffix, ".anim_mode"))
11997 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11998 string_has_parameter(value, "loop") ? ANIM_LOOP :
11999 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12000 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12001 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12002 string_has_parameter(value, "random") ? ANIM_RANDOM :
12003 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12004 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12005 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12006 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12007 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12008 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12009 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12010 string_has_parameter(value, "all") ? ANIM_ALL :
12011 string_has_parameter(value, "tiled") ? ANIM_TILED :
12012 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12015 if (string_has_parameter(value, "once"))
12016 result |= ANIM_ONCE;
12018 if (string_has_parameter(value, "reverse"))
12019 result |= ANIM_REVERSE;
12021 if (string_has_parameter(value, "opaque_player"))
12022 result |= ANIM_OPAQUE_PLAYER;
12024 if (string_has_parameter(value, "static_panel"))
12025 result |= ANIM_STATIC_PANEL;
12027 else if (strEqual(suffix, ".init_event") ||
12028 strEqual(suffix, ".anim_event"))
12030 result = get_anim_parameter_values(value);
12032 else if (strEqual(suffix, ".init_delay_action") ||
12033 strEqual(suffix, ".anim_delay_action") ||
12034 strEqual(suffix, ".post_delay_action") ||
12035 strEqual(suffix, ".init_event_action") ||
12036 strEqual(suffix, ".anim_event_action"))
12038 result = get_anim_action_parameter_value(value_raw);
12040 else if (strEqual(suffix, ".class"))
12042 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12043 get_hash_from_key(value));
12045 else if (strEqual(suffix, ".style"))
12047 result = STYLE_DEFAULT;
12049 if (string_has_parameter(value, "accurate_borders"))
12050 result |= STYLE_ACCURATE_BORDERS;
12052 if (string_has_parameter(value, "inner_corners"))
12053 result |= STYLE_INNER_CORNERS;
12055 if (string_has_parameter(value, "reverse"))
12056 result |= STYLE_REVERSE;
12058 if (string_has_parameter(value, "leftmost_position"))
12059 result |= STYLE_LEFTMOST_POSITION;
12061 if (string_has_parameter(value, "block_clicks"))
12062 result |= STYLE_BLOCK;
12064 if (string_has_parameter(value, "passthrough_clicks"))
12065 result |= STYLE_PASSTHROUGH;
12067 if (string_has_parameter(value, "multiple_actions"))
12068 result |= STYLE_MULTIPLE_ACTIONS;
12070 if (string_has_parameter(value, "consume_ce_event"))
12071 result |= STYLE_CONSUME_CE_EVENT;
12073 else if (strEqual(suffix, ".fade_mode"))
12075 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12076 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12077 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12078 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12079 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12080 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12081 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12082 FADE_MODE_DEFAULT);
12084 else if (strEqual(suffix, ".auto_delay_unit"))
12086 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12087 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12088 AUTO_DELAY_UNIT_DEFAULT);
12090 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12092 result = gfx.get_font_from_token_function(value);
12094 else // generic parameter of type integer or boolean
12096 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12097 type == TYPE_INTEGER ? get_integer_from_string(value) :
12098 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12099 ARG_UNDEFINED_VALUE);
12107 static int get_token_parameter_value(char *token, char *value_raw)
12111 if (token == NULL || value_raw == NULL)
12112 return ARG_UNDEFINED_VALUE;
12114 suffix = strrchr(token, '.');
12115 if (suffix == NULL)
12118 if (strEqual(suffix, ".element"))
12119 return getElementFromToken(value_raw);
12121 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12122 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12125 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12126 boolean ignore_defaults)
12130 for (i = 0; image_config_vars[i].token != NULL; i++)
12132 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12134 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12135 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12139 *image_config_vars[i].value =
12140 get_token_parameter_value(image_config_vars[i].token, value);
12144 void InitMenuDesignSettings_Static(void)
12146 // always start with reliable default values from static default config
12147 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12150 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12154 // the following initializes hierarchical values from static configuration
12156 // special case: initialize "ARG_DEFAULT" values in static default config
12157 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12158 titlescreen_initial_first_default.fade_mode =
12159 title_initial_first_default.fade_mode;
12160 titlescreen_initial_first_default.fade_delay =
12161 title_initial_first_default.fade_delay;
12162 titlescreen_initial_first_default.post_delay =
12163 title_initial_first_default.post_delay;
12164 titlescreen_initial_first_default.auto_delay =
12165 title_initial_first_default.auto_delay;
12166 titlescreen_initial_first_default.auto_delay_unit =
12167 title_initial_first_default.auto_delay_unit;
12168 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12169 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12170 titlescreen_first_default.post_delay = title_first_default.post_delay;
12171 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12172 titlescreen_first_default.auto_delay_unit =
12173 title_first_default.auto_delay_unit;
12174 titlemessage_initial_first_default.fade_mode =
12175 title_initial_first_default.fade_mode;
12176 titlemessage_initial_first_default.fade_delay =
12177 title_initial_first_default.fade_delay;
12178 titlemessage_initial_first_default.post_delay =
12179 title_initial_first_default.post_delay;
12180 titlemessage_initial_first_default.auto_delay =
12181 title_initial_first_default.auto_delay;
12182 titlemessage_initial_first_default.auto_delay_unit =
12183 title_initial_first_default.auto_delay_unit;
12184 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12185 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12186 titlemessage_first_default.post_delay = title_first_default.post_delay;
12187 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12188 titlemessage_first_default.auto_delay_unit =
12189 title_first_default.auto_delay_unit;
12191 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12192 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12193 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12194 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12195 titlescreen_initial_default.auto_delay_unit =
12196 title_initial_default.auto_delay_unit;
12197 titlescreen_default.fade_mode = title_default.fade_mode;
12198 titlescreen_default.fade_delay = title_default.fade_delay;
12199 titlescreen_default.post_delay = title_default.post_delay;
12200 titlescreen_default.auto_delay = title_default.auto_delay;
12201 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12202 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12203 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12204 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12205 titlemessage_initial_default.auto_delay_unit =
12206 title_initial_default.auto_delay_unit;
12207 titlemessage_default.fade_mode = title_default.fade_mode;
12208 titlemessage_default.fade_delay = title_default.fade_delay;
12209 titlemessage_default.post_delay = title_default.post_delay;
12210 titlemessage_default.auto_delay = title_default.auto_delay;
12211 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12213 // special case: initialize "ARG_DEFAULT" values in static default config
12214 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12215 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12217 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12218 titlescreen_first[i] = titlescreen_first_default;
12219 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12220 titlemessage_first[i] = titlemessage_first_default;
12222 titlescreen_initial[i] = titlescreen_initial_default;
12223 titlescreen[i] = titlescreen_default;
12224 titlemessage_initial[i] = titlemessage_initial_default;
12225 titlemessage[i] = titlemessage_default;
12228 // special case: initialize "ARG_DEFAULT" values in static default config
12229 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12230 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12232 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12235 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12236 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12237 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12240 // special case: initialize "ARG_DEFAULT" values in static default config
12241 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12242 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12244 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12245 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12246 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12248 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12251 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12255 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12259 struct XY *dst, *src;
12261 game_buttons_xy[] =
12263 { &game.button.save, &game.button.stop },
12264 { &game.button.pause2, &game.button.pause },
12265 { &game.button.load, &game.button.play },
12266 { &game.button.undo, &game.button.stop },
12267 { &game.button.redo, &game.button.play },
12273 // special case: initialize later added SETUP list size from LEVELS value
12274 if (menu.list_size[GAME_MODE_SETUP] == -1)
12275 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12277 // set default position for snapshot buttons to stop/pause/play buttons
12278 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12279 if ((*game_buttons_xy[i].dst).x == -1 &&
12280 (*game_buttons_xy[i].dst).y == -1)
12281 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12283 // --------------------------------------------------------------------------
12284 // dynamic viewports (including playfield margins, borders and alignments)
12285 // --------------------------------------------------------------------------
12287 // dynamic viewports currently only supported for landscape mode
12288 int display_width = MAX(video.display_width, video.display_height);
12289 int display_height = MIN(video.display_width, video.display_height);
12291 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12293 struct RectWithBorder *vp_window = &viewport.window[i];
12294 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12295 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12296 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12297 boolean dynamic_window_width = (vp_window->min_width != -1);
12298 boolean dynamic_window_height = (vp_window->min_height != -1);
12299 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12300 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12302 // adjust window size if min/max width/height is specified
12304 if (vp_window->min_width != -1)
12306 int window_width = display_width;
12308 // when using static window height, use aspect ratio of display
12309 if (vp_window->min_height == -1)
12310 window_width = vp_window->height * display_width / display_height;
12312 vp_window->width = MAX(vp_window->min_width, window_width);
12315 if (vp_window->min_height != -1)
12317 int window_height = display_height;
12319 // when using static window width, use aspect ratio of display
12320 if (vp_window->min_width == -1)
12321 window_height = vp_window->width * display_height / display_width;
12323 vp_window->height = MAX(vp_window->min_height, window_height);
12326 if (vp_window->max_width != -1)
12327 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12329 if (vp_window->max_height != -1)
12330 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12332 int playfield_width = vp_window->width;
12333 int playfield_height = vp_window->height;
12335 // adjust playfield size and position according to specified margins
12337 playfield_width -= vp_playfield->margin_left;
12338 playfield_width -= vp_playfield->margin_right;
12340 playfield_height -= vp_playfield->margin_top;
12341 playfield_height -= vp_playfield->margin_bottom;
12343 // adjust playfield size if min/max width/height is specified
12345 if (vp_playfield->min_width != -1)
12346 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12348 if (vp_playfield->min_height != -1)
12349 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12351 if (vp_playfield->max_width != -1)
12352 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12354 if (vp_playfield->max_height != -1)
12355 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12357 // adjust playfield position according to specified alignment
12359 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12360 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12361 else if (vp_playfield->align == ALIGN_CENTER)
12362 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12363 else if (vp_playfield->align == ALIGN_RIGHT)
12364 vp_playfield->x += playfield_width - vp_playfield->width;
12366 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12367 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12368 else if (vp_playfield->valign == VALIGN_MIDDLE)
12369 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12370 else if (vp_playfield->valign == VALIGN_BOTTOM)
12371 vp_playfield->y += playfield_height - vp_playfield->height;
12373 vp_playfield->x += vp_playfield->margin_left;
12374 vp_playfield->y += vp_playfield->margin_top;
12376 // adjust individual playfield borders if only default border is specified
12378 if (vp_playfield->border_left == -1)
12379 vp_playfield->border_left = vp_playfield->border_size;
12380 if (vp_playfield->border_right == -1)
12381 vp_playfield->border_right = vp_playfield->border_size;
12382 if (vp_playfield->border_top == -1)
12383 vp_playfield->border_top = vp_playfield->border_size;
12384 if (vp_playfield->border_bottom == -1)
12385 vp_playfield->border_bottom = vp_playfield->border_size;
12387 // set dynamic playfield borders if borders are specified as undefined
12388 // (but only if window size was dynamic and playfield size was static)
12390 if (dynamic_window_width && !dynamic_playfield_width)
12392 if (vp_playfield->border_left == -1)
12394 vp_playfield->border_left = (vp_playfield->x -
12395 vp_playfield->margin_left);
12396 vp_playfield->x -= vp_playfield->border_left;
12397 vp_playfield->width += vp_playfield->border_left;
12400 if (vp_playfield->border_right == -1)
12402 vp_playfield->border_right = (vp_window->width -
12404 vp_playfield->width -
12405 vp_playfield->margin_right);
12406 vp_playfield->width += vp_playfield->border_right;
12410 if (dynamic_window_height && !dynamic_playfield_height)
12412 if (vp_playfield->border_top == -1)
12414 vp_playfield->border_top = (vp_playfield->y -
12415 vp_playfield->margin_top);
12416 vp_playfield->y -= vp_playfield->border_top;
12417 vp_playfield->height += vp_playfield->border_top;
12420 if (vp_playfield->border_bottom == -1)
12422 vp_playfield->border_bottom = (vp_window->height -
12424 vp_playfield->height -
12425 vp_playfield->margin_bottom);
12426 vp_playfield->height += vp_playfield->border_bottom;
12430 // adjust playfield size to be a multiple of a defined alignment tile size
12432 int align_size = vp_playfield->align_size;
12433 int playfield_xtiles = vp_playfield->width / align_size;
12434 int playfield_ytiles = vp_playfield->height / align_size;
12435 int playfield_width_corrected = playfield_xtiles * align_size;
12436 int playfield_height_corrected = playfield_ytiles * align_size;
12437 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12438 i == GFX_SPECIAL_ARG_EDITOR);
12440 if (is_playfield_mode &&
12441 dynamic_playfield_width &&
12442 vp_playfield->width != playfield_width_corrected)
12444 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12446 vp_playfield->width = playfield_width_corrected;
12448 if (vp_playfield->align == ALIGN_LEFT)
12450 vp_playfield->border_left += playfield_xdiff;
12452 else if (vp_playfield->align == ALIGN_RIGHT)
12454 vp_playfield->border_right += playfield_xdiff;
12456 else if (vp_playfield->align == ALIGN_CENTER)
12458 int border_left_diff = playfield_xdiff / 2;
12459 int border_right_diff = playfield_xdiff - border_left_diff;
12461 vp_playfield->border_left += border_left_diff;
12462 vp_playfield->border_right += border_right_diff;
12466 if (is_playfield_mode &&
12467 dynamic_playfield_height &&
12468 vp_playfield->height != playfield_height_corrected)
12470 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12472 vp_playfield->height = playfield_height_corrected;
12474 if (vp_playfield->valign == VALIGN_TOP)
12476 vp_playfield->border_top += playfield_ydiff;
12478 else if (vp_playfield->align == VALIGN_BOTTOM)
12480 vp_playfield->border_right += playfield_ydiff;
12482 else if (vp_playfield->align == VALIGN_MIDDLE)
12484 int border_top_diff = playfield_ydiff / 2;
12485 int border_bottom_diff = playfield_ydiff - border_top_diff;
12487 vp_playfield->border_top += border_top_diff;
12488 vp_playfield->border_bottom += border_bottom_diff;
12492 // adjust door positions according to specified alignment
12494 for (j = 0; j < 2; j++)
12496 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12498 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12499 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12500 else if (vp_door->align == ALIGN_CENTER)
12501 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12502 else if (vp_door->align == ALIGN_RIGHT)
12503 vp_door->x += vp_window->width - vp_door->width;
12505 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12506 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12507 else if (vp_door->valign == VALIGN_MIDDLE)
12508 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12509 else if (vp_door->valign == VALIGN_BOTTOM)
12510 vp_door->y += vp_window->height - vp_door->height;
12515 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12519 struct XYTileSize *dst, *src;
12522 editor_buttons_xy[] =
12525 &editor.button.element_left, &editor.palette.element_left,
12526 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12529 &editor.button.element_middle, &editor.palette.element_middle,
12530 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12533 &editor.button.element_right, &editor.palette.element_right,
12534 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12541 // set default position for element buttons to element graphics
12542 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12544 if ((*editor_buttons_xy[i].dst).x == -1 &&
12545 (*editor_buttons_xy[i].dst).y == -1)
12547 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12549 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12551 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12555 // adjust editor palette rows and columns if specified to be dynamic
12557 if (editor.palette.cols == -1)
12559 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12560 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12561 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12563 editor.palette.cols = (vp_width - sc_width) / bt_width;
12565 if (editor.palette.x == -1)
12567 int palette_width = editor.palette.cols * bt_width + sc_width;
12569 editor.palette.x = (vp_width - palette_width) / 2;
12573 if (editor.palette.rows == -1)
12575 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12576 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12577 int tx_height = getFontHeight(FONT_TEXT_2);
12579 editor.palette.rows = (vp_height - tx_height) / bt_height;
12581 if (editor.palette.y == -1)
12583 int palette_height = editor.palette.rows * bt_height + tx_height;
12585 editor.palette.y = (vp_height - palette_height) / 2;
12590 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12591 boolean initialize)
12593 // special case: check if network and preview player positions are redefined,
12594 // to compare this later against the main menu level preview being redefined
12595 struct TokenIntPtrInfo menu_config_players[] =
12597 { "main.network_players.x", &menu.main.network_players.redefined },
12598 { "main.network_players.y", &menu.main.network_players.redefined },
12599 { "main.preview_players.x", &menu.main.preview_players.redefined },
12600 { "main.preview_players.y", &menu.main.preview_players.redefined },
12601 { "preview.x", &preview.redefined },
12602 { "preview.y", &preview.redefined }
12608 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12609 *menu_config_players[i].value = FALSE;
12613 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12614 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12615 *menu_config_players[i].value = TRUE;
12619 static void InitMenuDesignSettings_PreviewPlayers(void)
12621 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12624 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12626 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12629 static void LoadMenuDesignSettingsFromFilename(char *filename)
12631 static struct TitleFadingInfo tfi;
12632 static struct TitleMessageInfo tmi;
12633 static struct TokenInfo title_tokens[] =
12635 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12636 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12637 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12638 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12639 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12643 static struct TokenInfo titlemessage_tokens[] =
12645 { TYPE_INTEGER, &tmi.x, ".x" },
12646 { TYPE_INTEGER, &tmi.y, ".y" },
12647 { TYPE_INTEGER, &tmi.width, ".width" },
12648 { TYPE_INTEGER, &tmi.height, ".height" },
12649 { TYPE_INTEGER, &tmi.chars, ".chars" },
12650 { TYPE_INTEGER, &tmi.lines, ".lines" },
12651 { TYPE_INTEGER, &tmi.align, ".align" },
12652 { TYPE_INTEGER, &tmi.valign, ".valign" },
12653 { TYPE_INTEGER, &tmi.font, ".font" },
12654 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12655 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12656 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12657 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12658 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12659 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12660 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12661 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12662 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12668 struct TitleFadingInfo *info;
12673 // initialize first titles from "enter screen" definitions, if defined
12674 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12675 { &title_first_default, "menu.enter_screen.TITLE" },
12677 // initialize title screens from "next screen" definitions, if defined
12678 { &title_initial_default, "menu.next_screen.TITLE" },
12679 { &title_default, "menu.next_screen.TITLE" },
12685 struct TitleMessageInfo *array;
12688 titlemessage_arrays[] =
12690 // initialize first titles from "enter screen" definitions, if defined
12691 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12692 { titlescreen_first, "menu.enter_screen.TITLE" },
12693 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12694 { titlemessage_first, "menu.enter_screen.TITLE" },
12696 // initialize titles from "next screen" definitions, if defined
12697 { titlescreen_initial, "menu.next_screen.TITLE" },
12698 { titlescreen, "menu.next_screen.TITLE" },
12699 { titlemessage_initial, "menu.next_screen.TITLE" },
12700 { titlemessage, "menu.next_screen.TITLE" },
12702 // overwrite titles with title definitions, if defined
12703 { titlescreen_initial_first, "[title_initial]" },
12704 { titlescreen_first, "[title]" },
12705 { titlemessage_initial_first, "[title_initial]" },
12706 { titlemessage_first, "[title]" },
12708 { titlescreen_initial, "[title_initial]" },
12709 { titlescreen, "[title]" },
12710 { titlemessage_initial, "[title_initial]" },
12711 { titlemessage, "[title]" },
12713 // overwrite titles with title screen/message definitions, if defined
12714 { titlescreen_initial_first, "[titlescreen_initial]" },
12715 { titlescreen_first, "[titlescreen]" },
12716 { titlemessage_initial_first, "[titlemessage_initial]" },
12717 { titlemessage_first, "[titlemessage]" },
12719 { titlescreen_initial, "[titlescreen_initial]" },
12720 { titlescreen, "[titlescreen]" },
12721 { titlemessage_initial, "[titlemessage_initial]" },
12722 { titlemessage, "[titlemessage]" },
12726 SetupFileHash *setup_file_hash;
12729 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12732 // the following initializes hierarchical values from dynamic configuration
12734 // special case: initialize with default values that may be overwritten
12735 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12736 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12738 struct TokenIntPtrInfo menu_config[] =
12740 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12741 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12742 { "menu.list_size", &menu.list_size[i] }
12745 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12747 char *token = menu_config[j].token;
12748 char *value = getHashEntry(setup_file_hash, token);
12751 *menu_config[j].value = get_integer_from_string(value);
12755 // special case: initialize with default values that may be overwritten
12756 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12757 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12759 struct TokenIntPtrInfo menu_config[] =
12761 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12762 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12763 { "menu.list_size.INFO", &menu.list_size_info[i] },
12764 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12765 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12768 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12770 char *token = menu_config[j].token;
12771 char *value = getHashEntry(setup_file_hash, token);
12774 *menu_config[j].value = get_integer_from_string(value);
12778 // special case: initialize with default values that may be overwritten
12779 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12780 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12782 struct TokenIntPtrInfo menu_config[] =
12784 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12785 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12788 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12790 char *token = menu_config[j].token;
12791 char *value = getHashEntry(setup_file_hash, token);
12794 *menu_config[j].value = get_integer_from_string(value);
12798 // special case: initialize with default values that may be overwritten
12799 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12800 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12802 struct TokenIntPtrInfo menu_config[] =
12804 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12805 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12806 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12807 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12808 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12809 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12810 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12811 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12812 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12813 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12816 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12818 char *token = menu_config[j].token;
12819 char *value = getHashEntry(setup_file_hash, token);
12822 *menu_config[j].value = get_integer_from_string(value);
12826 // special case: initialize with default values that may be overwritten
12827 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12828 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12830 struct TokenIntPtrInfo menu_config[] =
12832 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12833 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12834 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12835 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12836 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12837 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12838 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12839 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12840 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12843 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12845 char *token = menu_config[j].token;
12846 char *value = getHashEntry(setup_file_hash, token);
12849 *menu_config[j].value = get_token_parameter_value(token, value);
12853 // special case: initialize with default values that may be overwritten
12854 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12855 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12859 char *token_prefix;
12860 struct RectWithBorder *struct_ptr;
12864 { "viewport.window", &viewport.window[i] },
12865 { "viewport.playfield", &viewport.playfield[i] },
12866 { "viewport.door_1", &viewport.door_1[i] },
12867 { "viewport.door_2", &viewport.door_2[i] }
12870 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12872 struct TokenIntPtrInfo vp_config[] =
12874 { ".x", &vp_struct[j].struct_ptr->x },
12875 { ".y", &vp_struct[j].struct_ptr->y },
12876 { ".width", &vp_struct[j].struct_ptr->width },
12877 { ".height", &vp_struct[j].struct_ptr->height },
12878 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12879 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12880 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12881 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12882 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12883 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12884 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12885 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12886 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12887 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12888 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12889 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12890 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12891 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12892 { ".align", &vp_struct[j].struct_ptr->align },
12893 { ".valign", &vp_struct[j].struct_ptr->valign }
12896 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12898 char *token = getStringCat2(vp_struct[j].token_prefix,
12899 vp_config[k].token);
12900 char *value = getHashEntry(setup_file_hash, token);
12903 *vp_config[k].value = get_token_parameter_value(token, value);
12910 // special case: initialize with default values that may be overwritten
12911 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12912 for (i = 0; title_info[i].info != NULL; i++)
12914 struct TitleFadingInfo *info = title_info[i].info;
12915 char *base_token = title_info[i].text;
12917 for (j = 0; title_tokens[j].type != -1; j++)
12919 char *token = getStringCat2(base_token, title_tokens[j].text);
12920 char *value = getHashEntry(setup_file_hash, token);
12924 int parameter_value = get_token_parameter_value(token, value);
12928 *(int *)title_tokens[j].value = (int)parameter_value;
12937 // special case: initialize with default values that may be overwritten
12938 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12939 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12941 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12942 char *base_token = titlemessage_arrays[i].text;
12944 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12946 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12947 char *value = getHashEntry(setup_file_hash, token);
12951 int parameter_value = get_token_parameter_value(token, value);
12953 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12957 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12958 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12960 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12970 // read (and overwrite with) values that may be specified in config file
12971 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12973 // special case: check if network and preview player positions are redefined
12974 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
12976 freeSetupFileHash(setup_file_hash);
12979 void LoadMenuDesignSettings(void)
12981 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12983 InitMenuDesignSettings_Static();
12984 InitMenuDesignSettings_SpecialPreProcessing();
12985 InitMenuDesignSettings_PreviewPlayers();
12987 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12989 // first look for special settings configured in level series config
12990 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12992 if (fileExists(filename_base))
12993 LoadMenuDesignSettingsFromFilename(filename_base);
12996 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12998 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12999 LoadMenuDesignSettingsFromFilename(filename_local);
13001 InitMenuDesignSettings_SpecialPostProcessing();
13004 void LoadMenuDesignSettings_AfterGraphics(void)
13006 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13009 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13010 boolean ignore_defaults)
13014 for (i = 0; sound_config_vars[i].token != NULL; i++)
13016 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13018 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13019 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13023 *sound_config_vars[i].value =
13024 get_token_parameter_value(sound_config_vars[i].token, value);
13028 void InitSoundSettings_Static(void)
13030 // always start with reliable default values from static default config
13031 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13034 static void LoadSoundSettingsFromFilename(char *filename)
13036 SetupFileHash *setup_file_hash;
13038 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13041 // read (and overwrite with) values that may be specified in config file
13042 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13044 freeSetupFileHash(setup_file_hash);
13047 void LoadSoundSettings(void)
13049 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13051 InitSoundSettings_Static();
13053 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13055 // first look for special settings configured in level series config
13056 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13058 if (fileExists(filename_base))
13059 LoadSoundSettingsFromFilename(filename_base);
13062 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13064 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13065 LoadSoundSettingsFromFilename(filename_local);
13068 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13070 char *filename = getEditorSetupFilename();
13071 SetupFileList *setup_file_list, *list;
13072 SetupFileHash *element_hash;
13073 int num_unknown_tokens = 0;
13076 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13079 element_hash = newSetupFileHash();
13081 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13082 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13084 // determined size may be larger than needed (due to unknown elements)
13086 for (list = setup_file_list; list != NULL; list = list->next)
13089 // add space for up to 3 more elements for padding that may be needed
13090 *num_elements += 3;
13092 // free memory for old list of elements, if needed
13093 checked_free(*elements);
13095 // allocate memory for new list of elements
13096 *elements = checked_malloc(*num_elements * sizeof(int));
13099 for (list = setup_file_list; list != NULL; list = list->next)
13101 char *value = getHashEntry(element_hash, list->token);
13103 if (value == NULL) // try to find obsolete token mapping
13105 char *mapped_token = get_mapped_token(list->token);
13107 if (mapped_token != NULL)
13109 value = getHashEntry(element_hash, mapped_token);
13111 free(mapped_token);
13117 (*elements)[(*num_elements)++] = atoi(value);
13121 if (num_unknown_tokens == 0)
13124 Warn("unknown token(s) found in config file:");
13125 Warn("- config file: '%s'", filename);
13127 num_unknown_tokens++;
13130 Warn("- token: '%s'", list->token);
13134 if (num_unknown_tokens > 0)
13137 while (*num_elements % 4) // pad with empty elements, if needed
13138 (*elements)[(*num_elements)++] = EL_EMPTY;
13140 freeSetupFileList(setup_file_list);
13141 freeSetupFileHash(element_hash);
13144 for (i = 0; i < *num_elements; i++)
13145 Debug("editor", "element '%s' [%d]\n",
13146 element_info[(*elements)[i]].token_name, (*elements)[i]);
13150 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13153 SetupFileHash *setup_file_hash = NULL;
13154 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13155 char *filename_music, *filename_prefix, *filename_info;
13161 token_to_value_ptr[] =
13163 { "title_header", &tmp_music_file_info.title_header },
13164 { "artist_header", &tmp_music_file_info.artist_header },
13165 { "album_header", &tmp_music_file_info.album_header },
13166 { "year_header", &tmp_music_file_info.year_header },
13167 { "played_header", &tmp_music_file_info.played_header },
13169 { "title", &tmp_music_file_info.title },
13170 { "artist", &tmp_music_file_info.artist },
13171 { "album", &tmp_music_file_info.album },
13172 { "year", &tmp_music_file_info.year },
13173 { "played", &tmp_music_file_info.played },
13179 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13180 getCustomMusicFilename(basename));
13182 if (filename_music == NULL)
13185 // ---------- try to replace file extension ----------
13187 filename_prefix = getStringCopy(filename_music);
13188 if (strrchr(filename_prefix, '.') != NULL)
13189 *strrchr(filename_prefix, '.') = '\0';
13190 filename_info = getStringCat2(filename_prefix, ".txt");
13192 if (fileExists(filename_info))
13193 setup_file_hash = loadSetupFileHash(filename_info);
13195 free(filename_prefix);
13196 free(filename_info);
13198 if (setup_file_hash == NULL)
13200 // ---------- try to add file extension ----------
13202 filename_prefix = getStringCopy(filename_music);
13203 filename_info = getStringCat2(filename_prefix, ".txt");
13205 if (fileExists(filename_info))
13206 setup_file_hash = loadSetupFileHash(filename_info);
13208 free(filename_prefix);
13209 free(filename_info);
13212 if (setup_file_hash == NULL)
13215 // ---------- music file info found ----------
13217 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13219 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13221 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13223 *token_to_value_ptr[i].value_ptr =
13224 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13227 tmp_music_file_info.basename = getStringCopy(basename);
13228 tmp_music_file_info.music = music;
13229 tmp_music_file_info.is_sound = is_sound;
13231 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13232 *new_music_file_info = tmp_music_file_info;
13234 return new_music_file_info;
13237 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13239 return get_music_file_info_ext(basename, music, FALSE);
13242 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13244 return get_music_file_info_ext(basename, sound, TRUE);
13247 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13248 char *basename, boolean is_sound)
13250 for (; list != NULL; list = list->next)
13251 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13257 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13259 return music_info_listed_ext(list, basename, FALSE);
13262 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13264 return music_info_listed_ext(list, basename, TRUE);
13267 void LoadMusicInfo(void)
13269 int num_music_noconf = getMusicListSize_NoConf();
13270 int num_music = getMusicListSize();
13271 int num_sounds = getSoundListSize();
13272 struct FileInfo *music, *sound;
13273 struct MusicFileInfo *next, **new;
13277 while (music_file_info != NULL)
13279 next = music_file_info->next;
13281 checked_free(music_file_info->basename);
13283 checked_free(music_file_info->title_header);
13284 checked_free(music_file_info->artist_header);
13285 checked_free(music_file_info->album_header);
13286 checked_free(music_file_info->year_header);
13287 checked_free(music_file_info->played_header);
13289 checked_free(music_file_info->title);
13290 checked_free(music_file_info->artist);
13291 checked_free(music_file_info->album);
13292 checked_free(music_file_info->year);
13293 checked_free(music_file_info->played);
13295 free(music_file_info);
13297 music_file_info = next;
13300 new = &music_file_info;
13302 // get (configured or unconfigured) music file info for all levels
13303 for (i = leveldir_current->first_level;
13304 i <= leveldir_current->last_level; i++)
13308 if (levelset.music[i] != MUS_UNDEFINED)
13310 // get music file info for configured level music
13311 music_nr = levelset.music[i];
13313 else if (num_music_noconf > 0)
13315 // get music file info for unconfigured level music
13316 int level_pos = i - leveldir_current->first_level;
13318 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13325 char *basename = getMusicInfoEntryFilename(music_nr);
13327 if (basename == NULL)
13330 if (!music_info_listed(music_file_info, basename))
13332 *new = get_music_file_info(basename, music_nr);
13335 new = &(*new)->next;
13339 // get music file info for all remaining configured music files
13340 for (i = 0; i < num_music; i++)
13342 music = getMusicListEntry(i);
13344 if (music->filename == NULL)
13347 if (strEqual(music->filename, UNDEFINED_FILENAME))
13350 // a configured file may be not recognized as music
13351 if (!FileIsMusic(music->filename))
13354 if (!music_info_listed(music_file_info, music->filename))
13356 *new = get_music_file_info(music->filename, i);
13359 new = &(*new)->next;
13363 // get sound file info for all configured sound files
13364 for (i = 0; i < num_sounds; i++)
13366 sound = getSoundListEntry(i);
13368 if (sound->filename == NULL)
13371 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13374 // a configured file may be not recognized as sound
13375 if (!FileIsSound(sound->filename))
13378 if (!sound_info_listed(music_file_info, sound->filename))
13380 *new = get_sound_file_info(sound->filename, i);
13382 new = &(*new)->next;
13386 // add pointers to previous list nodes
13388 struct MusicFileInfo *node = music_file_info;
13390 while (node != NULL)
13393 node->next->prev = node;
13399 static void add_helpanim_entry(int element, int action, int direction,
13400 int delay, int *num_list_entries)
13402 struct HelpAnimInfo *new_list_entry;
13403 (*num_list_entries)++;
13406 checked_realloc(helpanim_info,
13407 *num_list_entries * sizeof(struct HelpAnimInfo));
13408 new_list_entry = &helpanim_info[*num_list_entries - 1];
13410 new_list_entry->element = element;
13411 new_list_entry->action = action;
13412 new_list_entry->direction = direction;
13413 new_list_entry->delay = delay;
13416 static void print_unknown_token(char *filename, char *token, int token_nr)
13421 Warn("unknown token(s) found in config file:");
13422 Warn("- config file: '%s'", filename);
13425 Warn("- token: '%s'", token);
13428 static void print_unknown_token_end(int token_nr)
13434 void LoadHelpAnimInfo(void)
13436 char *filename = getHelpAnimFilename();
13437 SetupFileList *setup_file_list = NULL, *list;
13438 SetupFileHash *element_hash, *action_hash, *direction_hash;
13439 int num_list_entries = 0;
13440 int num_unknown_tokens = 0;
13443 if (fileExists(filename))
13444 setup_file_list = loadSetupFileList(filename);
13446 if (setup_file_list == NULL)
13448 // use reliable default values from static configuration
13449 SetupFileList *insert_ptr;
13451 insert_ptr = setup_file_list =
13452 newSetupFileList(helpanim_config[0].token,
13453 helpanim_config[0].value);
13455 for (i = 1; helpanim_config[i].token; i++)
13456 insert_ptr = addListEntry(insert_ptr,
13457 helpanim_config[i].token,
13458 helpanim_config[i].value);
13461 element_hash = newSetupFileHash();
13462 action_hash = newSetupFileHash();
13463 direction_hash = newSetupFileHash();
13465 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13466 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13468 for (i = 0; i < NUM_ACTIONS; i++)
13469 setHashEntry(action_hash, element_action_info[i].suffix,
13470 i_to_a(element_action_info[i].value));
13472 // do not store direction index (bit) here, but direction value!
13473 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13474 setHashEntry(direction_hash, element_direction_info[i].suffix,
13475 i_to_a(1 << element_direction_info[i].value));
13477 for (list = setup_file_list; list != NULL; list = list->next)
13479 char *element_token, *action_token, *direction_token;
13480 char *element_value, *action_value, *direction_value;
13481 int delay = atoi(list->value);
13483 if (strEqual(list->token, "end"))
13485 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13490 /* first try to break element into element/action/direction parts;
13491 if this does not work, also accept combined "element[.act][.dir]"
13492 elements (like "dynamite.active"), which are unique elements */
13494 if (strchr(list->token, '.') == NULL) // token contains no '.'
13496 element_value = getHashEntry(element_hash, list->token);
13497 if (element_value != NULL) // element found
13498 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13499 &num_list_entries);
13502 // no further suffixes found -- this is not an element
13503 print_unknown_token(filename, list->token, num_unknown_tokens++);
13509 // token has format "<prefix>.<something>"
13511 action_token = strchr(list->token, '.'); // suffix may be action ...
13512 direction_token = action_token; // ... or direction
13514 element_token = getStringCopy(list->token);
13515 *strchr(element_token, '.') = '\0';
13517 element_value = getHashEntry(element_hash, element_token);
13519 if (element_value == NULL) // this is no element
13521 element_value = getHashEntry(element_hash, list->token);
13522 if (element_value != NULL) // combined element found
13523 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13524 &num_list_entries);
13526 print_unknown_token(filename, list->token, num_unknown_tokens++);
13528 free(element_token);
13533 action_value = getHashEntry(action_hash, action_token);
13535 if (action_value != NULL) // action found
13537 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13538 &num_list_entries);
13540 free(element_token);
13545 direction_value = getHashEntry(direction_hash, direction_token);
13547 if (direction_value != NULL) // direction found
13549 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13550 &num_list_entries);
13552 free(element_token);
13557 if (strchr(action_token + 1, '.') == NULL)
13559 // no further suffixes found -- this is not an action nor direction
13561 element_value = getHashEntry(element_hash, list->token);
13562 if (element_value != NULL) // combined element found
13563 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13564 &num_list_entries);
13566 print_unknown_token(filename, list->token, num_unknown_tokens++);
13568 free(element_token);
13573 // token has format "<prefix>.<suffix>.<something>"
13575 direction_token = strchr(action_token + 1, '.');
13577 action_token = getStringCopy(action_token);
13578 *strchr(action_token + 1, '.') = '\0';
13580 action_value = getHashEntry(action_hash, action_token);
13582 if (action_value == NULL) // this is no action
13584 element_value = getHashEntry(element_hash, list->token);
13585 if (element_value != NULL) // combined element found
13586 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13587 &num_list_entries);
13589 print_unknown_token(filename, list->token, num_unknown_tokens++);
13591 free(element_token);
13592 free(action_token);
13597 direction_value = getHashEntry(direction_hash, direction_token);
13599 if (direction_value != NULL) // direction found
13601 add_helpanim_entry(atoi(element_value), atoi(action_value),
13602 atoi(direction_value), delay, &num_list_entries);
13604 free(element_token);
13605 free(action_token);
13610 // this is no direction
13612 element_value = getHashEntry(element_hash, list->token);
13613 if (element_value != NULL) // combined element found
13614 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13615 &num_list_entries);
13617 print_unknown_token(filename, list->token, num_unknown_tokens++);
13619 free(element_token);
13620 free(action_token);
13623 print_unknown_token_end(num_unknown_tokens);
13625 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13626 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13628 freeSetupFileList(setup_file_list);
13629 freeSetupFileHash(element_hash);
13630 freeSetupFileHash(action_hash);
13631 freeSetupFileHash(direction_hash);
13634 for (i = 0; i < num_list_entries; i++)
13635 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13636 EL_NAME(helpanim_info[i].element),
13637 helpanim_info[i].element,
13638 helpanim_info[i].action,
13639 helpanim_info[i].direction,
13640 helpanim_info[i].delay);
13644 void LoadHelpTextInfo(void)
13646 char *filename = getHelpTextFilename();
13649 if (helptext_info != NULL)
13651 freeSetupFileHash(helptext_info);
13652 helptext_info = NULL;
13655 if (fileExists(filename))
13656 helptext_info = loadSetupFileHash(filename);
13658 if (helptext_info == NULL)
13660 // use reliable default values from static configuration
13661 helptext_info = newSetupFileHash();
13663 for (i = 0; helptext_config[i].token; i++)
13664 setHashEntry(helptext_info,
13665 helptext_config[i].token,
13666 helptext_config[i].value);
13670 BEGIN_HASH_ITERATION(helptext_info, itr)
13672 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13673 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13675 END_HASH_ITERATION(hash, itr)
13680 // ----------------------------------------------------------------------------
13682 // ----------------------------------------------------------------------------
13684 #define MAX_NUM_CONVERT_LEVELS 1000
13686 void ConvertLevels(void)
13688 static LevelDirTree *convert_leveldir = NULL;
13689 static int convert_level_nr = -1;
13690 static int num_levels_handled = 0;
13691 static int num_levels_converted = 0;
13692 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13695 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13696 global.convert_leveldir);
13698 if (convert_leveldir == NULL)
13699 Fail("no such level identifier: '%s'", global.convert_leveldir);
13701 leveldir_current = convert_leveldir;
13703 if (global.convert_level_nr != -1)
13705 convert_leveldir->first_level = global.convert_level_nr;
13706 convert_leveldir->last_level = global.convert_level_nr;
13709 convert_level_nr = convert_leveldir->first_level;
13711 PrintLine("=", 79);
13712 Print("Converting levels\n");
13713 PrintLine("-", 79);
13714 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13715 Print("Level series name: '%s'\n", convert_leveldir->name);
13716 Print("Level series author: '%s'\n", convert_leveldir->author);
13717 Print("Number of levels: %d\n", convert_leveldir->levels);
13718 PrintLine("=", 79);
13721 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13722 levels_failed[i] = FALSE;
13724 while (convert_level_nr <= convert_leveldir->last_level)
13726 char *level_filename;
13729 level_nr = convert_level_nr++;
13731 Print("Level %03d: ", level_nr);
13733 LoadLevel(level_nr);
13734 if (level.no_level_file || level.no_valid_file)
13736 Print("(no level)\n");
13740 Print("converting level ... ");
13743 // special case: conversion of some EMC levels as requested by ACME
13744 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13747 level_filename = getDefaultLevelFilename(level_nr);
13748 new_level = !fileExists(level_filename);
13752 SaveLevel(level_nr);
13754 num_levels_converted++;
13756 Print("converted.\n");
13760 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13761 levels_failed[level_nr] = TRUE;
13763 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13766 num_levels_handled++;
13770 PrintLine("=", 79);
13771 Print("Number of levels handled: %d\n", num_levels_handled);
13772 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13773 (num_levels_handled ?
13774 num_levels_converted * 100 / num_levels_handled : 0));
13775 PrintLine("-", 79);
13776 Print("Summary (for automatic parsing by scripts):\n");
13777 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13778 convert_leveldir->identifier, num_levels_converted,
13779 num_levels_handled,
13780 (num_levels_handled ?
13781 num_levels_converted * 100 / num_levels_handled : 0));
13783 if (num_levels_handled != num_levels_converted)
13785 Print(", FAILED:");
13786 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13787 if (levels_failed[i])
13792 PrintLine("=", 79);
13794 CloseAllAndExit(0);
13798 // ----------------------------------------------------------------------------
13799 // create and save images for use in level sketches (raw BMP format)
13800 // ----------------------------------------------------------------------------
13802 void CreateLevelSketchImages(void)
13808 InitElementPropertiesGfxElement();
13810 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13811 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13813 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13815 int element = getMappedElement(i);
13816 char basename1[16];
13817 char basename2[16];
13821 sprintf(basename1, "%04d.bmp", i);
13822 sprintf(basename2, "%04ds.bmp", i);
13824 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13825 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13827 DrawSizedElement(0, 0, element, TILESIZE);
13828 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13830 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13831 Fail("cannot save level sketch image file '%s'", filename1);
13833 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13834 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13836 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13837 Fail("cannot save level sketch image file '%s'", filename2);
13842 // create corresponding SQL statements (for normal and small images)
13845 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13846 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13849 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13850 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13852 // optional: create content for forum level sketch demonstration post
13854 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13857 FreeBitmap(bitmap1);
13858 FreeBitmap(bitmap2);
13861 fprintf(stderr, "\n");
13863 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13865 CloseAllAndExit(0);
13869 // ----------------------------------------------------------------------------
13870 // create and save images for element collecting animations (raw BMP format)
13871 // ----------------------------------------------------------------------------
13873 static boolean createCollectImage(int element)
13875 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13878 void CreateCollectElementImages(void)
13882 int anim_frames = num_steps - 1;
13883 int tile_size = TILESIZE;
13884 int anim_width = tile_size * anim_frames;
13885 int anim_height = tile_size;
13886 int num_collect_images = 0;
13887 int pos_collect_images = 0;
13889 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13890 if (createCollectImage(i))
13891 num_collect_images++;
13893 Info("Creating %d element collecting animation images ...",
13894 num_collect_images);
13896 int dst_width = anim_width * 2;
13897 int dst_height = anim_height * num_collect_images / 2;
13898 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13899 char *basename_bmp = "RocksCollect.bmp";
13900 char *basename_png = "RocksCollect.png";
13901 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13902 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13903 int len_filename_bmp = strlen(filename_bmp);
13904 int len_filename_png = strlen(filename_png);
13905 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13906 char cmd_convert[max_command_len];
13908 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13912 // force using RGBA surface for destination bitmap
13913 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13914 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13916 dst_bitmap->surface =
13917 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13919 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13921 if (!createCollectImage(i))
13924 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13925 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13926 int graphic = el2img(i);
13927 char *token_name = element_info[i].token_name;
13928 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13929 Bitmap *src_bitmap;
13932 Info("- creating collecting image for '%s' ...", token_name);
13934 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13936 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13937 tile_size, tile_size, 0, 0);
13939 // force using RGBA surface for temporary bitmap (using transparent black)
13940 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13941 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13943 tmp_bitmap->surface =
13944 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13946 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13948 for (j = 0; j < anim_frames; j++)
13950 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13951 int frame_size = frame_size_final * num_steps;
13952 int offset = (tile_size - frame_size_final) / 2;
13953 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13955 while (frame_size > frame_size_final)
13959 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13961 FreeBitmap(frame_bitmap);
13963 frame_bitmap = half_bitmap;
13966 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13967 frame_size_final, frame_size_final,
13968 dst_x + j * tile_size + offset, dst_y + offset);
13970 FreeBitmap(frame_bitmap);
13973 tmp_bitmap->surface_masked = NULL;
13975 FreeBitmap(tmp_bitmap);
13977 pos_collect_images++;
13980 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13981 Fail("cannot save element collecting image file '%s'", filename_bmp);
13983 FreeBitmap(dst_bitmap);
13985 Info("Converting image file from BMP to PNG ...");
13987 if (system(cmd_convert) != 0)
13988 Fail("converting image file failed");
13990 unlink(filename_bmp);
13994 CloseAllAndExit(0);
13998 // ----------------------------------------------------------------------------
13999 // create and save images for custom and group elements (raw BMP format)
14000 // ----------------------------------------------------------------------------
14002 void CreateCustomElementImages(char *directory)
14004 char *src_basename = "RocksCE-template.ilbm";
14005 char *dst_basename = "RocksCE.bmp";
14006 char *src_filename = getPath2(directory, src_basename);
14007 char *dst_filename = getPath2(directory, dst_basename);
14008 Bitmap *src_bitmap;
14010 int yoffset_ce = 0;
14011 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14014 InitVideoDefaults();
14016 ReCreateBitmap(&backbuffer, video.width, video.height);
14018 src_bitmap = LoadImage(src_filename);
14020 bitmap = CreateBitmap(TILEX * 16 * 2,
14021 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14024 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14031 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14032 TILEX * x, TILEY * y + yoffset_ce);
14034 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14036 TILEX * x + TILEX * 16,
14037 TILEY * y + yoffset_ce);
14039 for (j = 2; j >= 0; j--)
14043 BlitBitmap(src_bitmap, bitmap,
14044 TILEX + c * 7, 0, 6, 10,
14045 TILEX * x + 6 + j * 7,
14046 TILEY * y + 11 + yoffset_ce);
14048 BlitBitmap(src_bitmap, bitmap,
14049 TILEX + c * 8, TILEY, 6, 10,
14050 TILEX * 16 + TILEX * x + 6 + j * 8,
14051 TILEY * y + 10 + yoffset_ce);
14057 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14064 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14065 TILEX * x, TILEY * y + yoffset_ge);
14067 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14069 TILEX * x + TILEX * 16,
14070 TILEY * y + yoffset_ge);
14072 for (j = 1; j >= 0; j--)
14076 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14077 TILEX * x + 6 + j * 10,
14078 TILEY * y + 11 + yoffset_ge);
14080 BlitBitmap(src_bitmap, bitmap,
14081 TILEX + c * 8, TILEY + 12, 6, 10,
14082 TILEX * 16 + TILEX * x + 10 + j * 8,
14083 TILEY * y + 10 + yoffset_ge);
14089 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14090 Fail("cannot save CE graphics file '%s'", dst_filename);
14092 FreeBitmap(bitmap);
14094 CloseAllAndExit(0);