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 EL_MM_STEEL_BLOCK, -1,
927 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
928 &li.mm_time_block, 75
932 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
933 &li.score[SC_ELEM_BONUS], 10
936 // ---------- unused values -------------------------------------------------
939 EL_UNKNOWN, SAVE_CONF_NEVER,
940 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
941 &li.score[SC_UNKNOWN_15], 10
951 static struct LevelFileConfigInfo chunk_config_NOTE[] =
955 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
956 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
960 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
961 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
966 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
967 &xx_envelope.autowrap, FALSE
971 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
972 &xx_envelope.centered, FALSE
977 TYPE_STRING, CONF_VALUE_BYTES(1),
978 &xx_envelope.text, -1, NULL,
979 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
980 &xx_default_string_empty[0]
990 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
994 TYPE_STRING, CONF_VALUE_BYTES(1),
995 &xx_ei.description[0], -1,
996 &yy_ei.description[0],
997 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
998 &xx_default_description[0]
1003 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1004 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1005 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1007 #if ENABLE_RESERVED_CODE
1008 // (reserved for later use)
1011 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1012 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1013 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1019 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1020 &xx_ei.use_gfx_element, FALSE,
1021 &yy_ei.use_gfx_element
1025 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1026 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1027 &yy_ei.gfx_element_initial
1032 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1033 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1034 &yy_ei.access_direction
1039 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1040 &xx_ei.collect_score_initial, 10,
1041 &yy_ei.collect_score_initial
1045 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1046 &xx_ei.collect_count_initial, 1,
1047 &yy_ei.collect_count_initial
1052 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1053 &xx_ei.ce_value_fixed_initial, 0,
1054 &yy_ei.ce_value_fixed_initial
1058 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1059 &xx_ei.ce_value_random_initial, 0,
1060 &yy_ei.ce_value_random_initial
1064 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1065 &xx_ei.use_last_ce_value, FALSE,
1066 &yy_ei.use_last_ce_value
1071 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1072 &xx_ei.push_delay_fixed, 8,
1073 &yy_ei.push_delay_fixed
1077 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1078 &xx_ei.push_delay_random, 8,
1079 &yy_ei.push_delay_random
1083 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1084 &xx_ei.drop_delay_fixed, 0,
1085 &yy_ei.drop_delay_fixed
1089 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1090 &xx_ei.drop_delay_random, 0,
1091 &yy_ei.drop_delay_random
1095 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1096 &xx_ei.move_delay_fixed, 0,
1097 &yy_ei.move_delay_fixed
1101 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1102 &xx_ei.move_delay_random, 0,
1103 &yy_ei.move_delay_random
1107 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1108 &xx_ei.step_delay_fixed, 0,
1109 &yy_ei.step_delay_fixed
1113 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1114 &xx_ei.step_delay_random, 0,
1115 &yy_ei.step_delay_random
1120 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1121 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1126 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1127 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1128 &yy_ei.move_direction_initial
1132 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1133 &xx_ei.move_stepsize, TILEX / 8,
1134 &yy_ei.move_stepsize
1139 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1140 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1141 &yy_ei.move_enter_element
1145 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1146 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1147 &yy_ei.move_leave_element
1151 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1152 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1153 &yy_ei.move_leave_type
1158 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1159 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1160 &yy_ei.slippery_type
1165 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1166 &xx_ei.explosion_type, EXPLODES_3X3,
1167 &yy_ei.explosion_type
1171 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1172 &xx_ei.explosion_delay, 16,
1173 &yy_ei.explosion_delay
1177 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1178 &xx_ei.ignition_delay, 8,
1179 &yy_ei.ignition_delay
1184 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1185 &xx_ei.content, EL_EMPTY_SPACE,
1187 &xx_num_contents, 1, 1
1190 // ---------- "num_change_pages" must be the last entry ---------------------
1193 -1, SAVE_CONF_ALWAYS,
1194 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1195 &xx_ei.num_change_pages, 1,
1196 &yy_ei.num_change_pages
1207 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1209 // ---------- "current_change_page" must be the first entry -----------------
1212 -1, SAVE_CONF_ALWAYS,
1213 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1214 &xx_current_change_page, -1
1217 // ---------- (the remaining entries can be in any order) -------------------
1221 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1222 &xx_change.can_change, FALSE
1227 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1228 &xx_event_bits[0], 0
1232 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1233 &xx_event_bits[1], 0
1238 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1239 &xx_change.trigger_player, CH_PLAYER_ANY
1243 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1244 &xx_change.trigger_side, CH_SIDE_ANY
1248 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1249 &xx_change.trigger_page, CH_PAGE_ANY
1254 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1255 &xx_change.target_element, EL_EMPTY_SPACE
1260 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1261 &xx_change.delay_fixed, 0
1265 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1266 &xx_change.delay_random, 0
1270 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1271 &xx_change.delay_frames, FRAMES_PER_SECOND
1276 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1277 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1282 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1283 &xx_change.explode, FALSE
1287 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1288 &xx_change.use_target_content, FALSE
1292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1293 &xx_change.only_if_complete, FALSE
1297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1298 &xx_change.use_random_replace, FALSE
1302 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1303 &xx_change.random_percentage, 100
1307 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1308 &xx_change.replace_when, CP_WHEN_EMPTY
1313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1314 &xx_change.has_action, FALSE
1318 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1319 &xx_change.action_type, CA_NO_ACTION
1323 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1324 &xx_change.action_mode, CA_MODE_UNDEFINED
1328 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1329 &xx_change.action_arg, CA_ARG_UNDEFINED
1334 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1335 &xx_change.action_element, EL_EMPTY_SPACE
1340 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1341 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1342 &xx_num_contents, 1, 1
1352 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1356 TYPE_STRING, CONF_VALUE_BYTES(1),
1357 &xx_ei.description[0], -1, NULL,
1358 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1359 &xx_default_description[0]
1364 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1365 &xx_ei.use_gfx_element, FALSE
1369 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1370 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1375 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1376 &xx_group.choice_mode, ANIM_RANDOM
1381 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1382 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1383 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1393 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1398 &xx_ei.use_gfx_element, FALSE
1402 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1403 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1413 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1418 &li.block_snap_field, TRUE
1422 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1423 &li.continuous_snapping, TRUE
1427 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1428 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1433 &li.use_start_element[0], FALSE
1437 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1438 &li.start_element[0], EL_PLAYER_1
1442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1443 &li.use_artwork_element[0], FALSE
1447 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1448 &li.artwork_element[0], EL_PLAYER_1
1452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1453 &li.use_explosion_element[0], FALSE
1457 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1458 &li.explosion_element[0], EL_PLAYER_1
1473 filetype_id_list[] =
1475 { LEVEL_FILE_TYPE_RND, "RND" },
1476 { LEVEL_FILE_TYPE_BD, "BD" },
1477 { LEVEL_FILE_TYPE_EM, "EM" },
1478 { LEVEL_FILE_TYPE_SP, "SP" },
1479 { LEVEL_FILE_TYPE_DX, "DX" },
1480 { LEVEL_FILE_TYPE_SB, "SB" },
1481 { LEVEL_FILE_TYPE_DC, "DC" },
1482 { LEVEL_FILE_TYPE_MM, "MM" },
1483 { LEVEL_FILE_TYPE_MM, "DF" },
1488 // ============================================================================
1489 // level file functions
1490 // ============================================================================
1492 static boolean check_special_flags(char *flag)
1494 if (strEqual(options.special_flags, flag) ||
1495 strEqual(leveldir_current->special_flags, flag))
1501 static struct DateInfo getCurrentDate(void)
1503 time_t epoch_seconds = time(NULL);
1504 struct tm *now = localtime(&epoch_seconds);
1505 struct DateInfo date;
1507 date.year = now->tm_year + 1900;
1508 date.month = now->tm_mon + 1;
1509 date.day = now->tm_mday;
1511 date.src = DATE_SRC_CLOCK;
1516 static void resetEventFlags(struct ElementChangeInfo *change)
1520 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1521 change->has_event[i] = FALSE;
1524 static void resetEventBits(void)
1528 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1529 xx_event_bits[i] = 0;
1532 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1536 /* important: only change event flag if corresponding event bit is set
1537 (this is because all xx_event_bits[] values are loaded separately,
1538 and all xx_event_bits[] values are set back to zero before loading
1539 another value xx_event_bits[x] (each value representing 32 flags)) */
1541 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1542 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1543 change->has_event[i] = TRUE;
1546 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1550 /* in contrast to the above function setEventFlagsFromEventBits(), it
1551 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1552 depending on the corresponding change->has_event[i] values here, as
1553 all xx_event_bits[] values are reset in resetEventBits() before */
1555 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1556 if (change->has_event[i])
1557 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1560 static char *getDefaultElementDescription(struct ElementInfo *ei)
1562 static char description[MAX_ELEMENT_NAME_LEN + 1];
1563 char *default_description = (ei->custom_description != NULL ?
1564 ei->custom_description :
1565 ei->editor_description);
1568 // always start with reliable default values
1569 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1570 description[i] = '\0';
1572 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1573 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1575 return &description[0];
1578 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1580 char *default_description = getDefaultElementDescription(ei);
1583 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1584 ei->description[i] = default_description[i];
1587 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1591 for (i = 0; conf[i].data_type != -1; i++)
1593 int default_value = conf[i].default_value;
1594 int data_type = conf[i].data_type;
1595 int conf_type = conf[i].conf_type;
1596 int byte_mask = conf_type & CONF_MASK_BYTES;
1598 if (byte_mask == CONF_MASK_MULTI_BYTES)
1600 int default_num_entities = conf[i].default_num_entities;
1601 int max_num_entities = conf[i].max_num_entities;
1603 *(int *)(conf[i].num_entities) = default_num_entities;
1605 if (data_type == TYPE_STRING)
1607 char *default_string = conf[i].default_string;
1608 char *string = (char *)(conf[i].value);
1610 strncpy(string, default_string, max_num_entities);
1612 else if (data_type == TYPE_ELEMENT_LIST)
1614 int *element_array = (int *)(conf[i].value);
1617 for (j = 0; j < max_num_entities; j++)
1618 element_array[j] = default_value;
1620 else if (data_type == TYPE_CONTENT_LIST)
1622 struct Content *content = (struct Content *)(conf[i].value);
1625 for (c = 0; c < max_num_entities; c++)
1626 for (y = 0; y < 3; y++)
1627 for (x = 0; x < 3; x++)
1628 content[c].e[x][y] = default_value;
1631 else // constant size configuration data (1, 2 or 4 bytes)
1633 if (data_type == TYPE_BOOLEAN)
1634 *(boolean *)(conf[i].value) = default_value;
1636 *(int *) (conf[i].value) = default_value;
1641 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1645 for (i = 0; conf[i].data_type != -1; i++)
1647 int data_type = conf[i].data_type;
1648 int conf_type = conf[i].conf_type;
1649 int byte_mask = conf_type & CONF_MASK_BYTES;
1651 if (byte_mask == CONF_MASK_MULTI_BYTES)
1653 int max_num_entities = conf[i].max_num_entities;
1655 if (data_type == TYPE_STRING)
1657 char *string = (char *)(conf[i].value);
1658 char *string_copy = (char *)(conf[i].value_copy);
1660 strncpy(string_copy, string, max_num_entities);
1662 else if (data_type == TYPE_ELEMENT_LIST)
1664 int *element_array = (int *)(conf[i].value);
1665 int *element_array_copy = (int *)(conf[i].value_copy);
1668 for (j = 0; j < max_num_entities; j++)
1669 element_array_copy[j] = element_array[j];
1671 else if (data_type == TYPE_CONTENT_LIST)
1673 struct Content *content = (struct Content *)(conf[i].value);
1674 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1677 for (c = 0; c < max_num_entities; c++)
1678 for (y = 0; y < 3; y++)
1679 for (x = 0; x < 3; x++)
1680 content_copy[c].e[x][y] = content[c].e[x][y];
1683 else // constant size configuration data (1, 2 or 4 bytes)
1685 if (data_type == TYPE_BOOLEAN)
1686 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1688 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1693 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1697 xx_ei = *ei_from; // copy element data into temporary buffer
1698 yy_ei = *ei_to; // copy element data into temporary buffer
1700 copyConfigFromConfigList(chunk_config_CUSX_base);
1705 // ---------- reinitialize and copy change pages ----------
1707 ei_to->num_change_pages = ei_from->num_change_pages;
1708 ei_to->current_change_page = ei_from->current_change_page;
1710 setElementChangePages(ei_to, ei_to->num_change_pages);
1712 for (i = 0; i < ei_to->num_change_pages; i++)
1713 ei_to->change_page[i] = ei_from->change_page[i];
1715 // ---------- copy group element info ----------
1716 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1717 *ei_to->group = *ei_from->group;
1719 // mark this custom element as modified
1720 ei_to->modified_settings = TRUE;
1723 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1725 int change_page_size = sizeof(struct ElementChangeInfo);
1727 ei->num_change_pages = MAX(1, change_pages);
1730 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1732 if (ei->current_change_page >= ei->num_change_pages)
1733 ei->current_change_page = ei->num_change_pages - 1;
1735 ei->change = &ei->change_page[ei->current_change_page];
1738 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1740 xx_change = *change; // copy change data into temporary buffer
1742 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1744 *change = xx_change;
1746 resetEventFlags(change);
1748 change->direct_action = 0;
1749 change->other_action = 0;
1751 change->pre_change_function = NULL;
1752 change->change_function = NULL;
1753 change->post_change_function = NULL;
1756 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1760 li = *level; // copy level data into temporary buffer
1761 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1762 *level = li; // copy temporary buffer back to level data
1764 setLevelInfoToDefaults_EM();
1765 setLevelInfoToDefaults_SP();
1766 setLevelInfoToDefaults_MM();
1768 level->native_em_level = &native_em_level;
1769 level->native_sp_level = &native_sp_level;
1770 level->native_mm_level = &native_mm_level;
1772 level->file_version = FILE_VERSION_ACTUAL;
1773 level->game_version = GAME_VERSION_ACTUAL;
1775 level->creation_date = getCurrentDate();
1777 level->encoding_16bit_field = TRUE;
1778 level->encoding_16bit_yamyam = TRUE;
1779 level->encoding_16bit_amoeba = TRUE;
1781 // clear level name and level author string buffers
1782 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1783 level->name[i] = '\0';
1784 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1785 level->author[i] = '\0';
1787 // set level name and level author to default values
1788 strcpy(level->name, NAMELESS_LEVEL_NAME);
1789 strcpy(level->author, ANONYMOUS_NAME);
1791 // set level playfield to playable default level with player and exit
1792 for (x = 0; x < MAX_LEV_FIELDX; x++)
1793 for (y = 0; y < MAX_LEV_FIELDY; y++)
1794 level->field[x][y] = EL_SAND;
1796 level->field[0][0] = EL_PLAYER_1;
1797 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1799 BorderElement = EL_STEELWALL;
1801 // detect custom elements when loading them
1802 level->file_has_custom_elements = FALSE;
1804 // set all bug compatibility flags to "false" => do not emulate this bug
1805 level->use_action_after_change_bug = FALSE;
1807 if (leveldir_current)
1809 // try to determine better author name than 'anonymous'
1810 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1812 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1813 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1817 switch (LEVELCLASS(leveldir_current))
1819 case LEVELCLASS_TUTORIAL:
1820 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1823 case LEVELCLASS_CONTRIB:
1824 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1825 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1828 case LEVELCLASS_PRIVATE:
1829 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1830 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1834 // keep default value
1841 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1843 static boolean clipboard_elements_initialized = FALSE;
1846 InitElementPropertiesStatic();
1848 li = *level; // copy level data into temporary buffer
1849 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1850 *level = li; // copy temporary buffer back to level data
1852 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1855 struct ElementInfo *ei = &element_info[element];
1857 if (element == EL_MM_GRAY_BALL)
1859 struct LevelInfo_MM *level_mm = level->native_mm_level;
1862 for (j = 0; j < level->num_mm_ball_contents; j++)
1863 level->mm_ball_content[j] =
1864 map_element_MM_to_RND(level_mm->ball_content[j]);
1867 // never initialize clipboard elements after the very first time
1868 // (to be able to use clipboard elements between several levels)
1869 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1872 if (IS_ENVELOPE(element))
1874 int envelope_nr = element - EL_ENVELOPE_1;
1876 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1878 level->envelope[envelope_nr] = xx_envelope;
1881 if (IS_CUSTOM_ELEMENT(element) ||
1882 IS_GROUP_ELEMENT(element) ||
1883 IS_INTERNAL_ELEMENT(element))
1885 xx_ei = *ei; // copy element data into temporary buffer
1887 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1892 setElementChangePages(ei, 1);
1893 setElementChangeInfoToDefaults(ei->change);
1895 if (IS_CUSTOM_ELEMENT(element) ||
1896 IS_GROUP_ELEMENT(element) ||
1897 IS_INTERNAL_ELEMENT(element))
1899 setElementDescriptionToDefault(ei);
1901 ei->modified_settings = FALSE;
1904 if (IS_CUSTOM_ELEMENT(element) ||
1905 IS_INTERNAL_ELEMENT(element))
1907 // internal values used in level editor
1909 ei->access_type = 0;
1910 ei->access_layer = 0;
1911 ei->access_protected = 0;
1912 ei->walk_to_action = 0;
1913 ei->smash_targets = 0;
1916 ei->can_explode_by_fire = FALSE;
1917 ei->can_explode_smashed = FALSE;
1918 ei->can_explode_impact = FALSE;
1920 ei->current_change_page = 0;
1923 if (IS_GROUP_ELEMENT(element) ||
1924 IS_INTERNAL_ELEMENT(element))
1926 struct ElementGroupInfo *group;
1928 // initialize memory for list of elements in group
1929 if (ei->group == NULL)
1930 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1934 xx_group = *group; // copy group data into temporary buffer
1936 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1941 if (IS_EMPTY_ELEMENT(element) ||
1942 IS_INTERNAL_ELEMENT(element))
1944 xx_ei = *ei; // copy element data into temporary buffer
1946 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1952 clipboard_elements_initialized = TRUE;
1955 static void setLevelInfoToDefaults(struct LevelInfo *level,
1956 boolean level_info_only,
1957 boolean reset_file_status)
1959 setLevelInfoToDefaults_Level(level);
1961 if (!level_info_only)
1962 setLevelInfoToDefaults_Elements(level);
1964 if (reset_file_status)
1966 level->no_valid_file = FALSE;
1967 level->no_level_file = FALSE;
1970 level->changed = FALSE;
1973 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1975 level_file_info->nr = 0;
1976 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1977 level_file_info->packed = FALSE;
1979 setString(&level_file_info->basename, NULL);
1980 setString(&level_file_info->filename, NULL);
1983 int getMappedElement_SB(int, boolean);
1985 static void ActivateLevelTemplate(void)
1989 if (check_special_flags("load_xsb_to_ces"))
1991 // fill smaller playfields with padding "beyond border wall" elements
1992 if (level.fieldx < level_template.fieldx ||
1993 level.fieldy < level_template.fieldy)
1995 short field[level.fieldx][level.fieldy];
1996 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1997 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1998 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1999 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2001 // copy old playfield (which is smaller than the visible area)
2002 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2003 field[x][y] = level.field[x][y];
2005 // fill new, larger playfield with "beyond border wall" elements
2006 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2007 level.field[x][y] = getMappedElement_SB('_', TRUE);
2009 // copy the old playfield to the middle of the new playfield
2010 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2011 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2013 level.fieldx = new_fieldx;
2014 level.fieldy = new_fieldy;
2018 // Currently there is no special action needed to activate the template
2019 // data, because 'element_info' property settings overwrite the original
2020 // level data, while all other variables do not change.
2022 // Exception: 'from_level_template' elements in the original level playfield
2023 // are overwritten with the corresponding elements at the same position in
2024 // playfield from the level template.
2026 for (x = 0; x < level.fieldx; x++)
2027 for (y = 0; y < level.fieldy; y++)
2028 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2029 level.field[x][y] = level_template.field[x][y];
2031 if (check_special_flags("load_xsb_to_ces"))
2033 struct LevelInfo level_backup = level;
2035 // overwrite all individual level settings from template level settings
2036 level = level_template;
2038 // restore level file info
2039 level.file_info = level_backup.file_info;
2041 // restore playfield size
2042 level.fieldx = level_backup.fieldx;
2043 level.fieldy = level_backup.fieldy;
2045 // restore playfield content
2046 for (x = 0; x < level.fieldx; x++)
2047 for (y = 0; y < level.fieldy; y++)
2048 level.field[x][y] = level_backup.field[x][y];
2050 // restore name and author from individual level
2051 strcpy(level.name, level_backup.name);
2052 strcpy(level.author, level_backup.author);
2054 // restore flag "use_custom_template"
2055 level.use_custom_template = level_backup.use_custom_template;
2059 static char *getLevelFilenameFromBasename(char *basename)
2061 static char *filename = NULL;
2063 checked_free(filename);
2065 filename = getPath2(getCurrentLevelDir(), basename);
2070 static int getFileTypeFromBasename(char *basename)
2072 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2074 static char *filename = NULL;
2075 struct stat file_status;
2077 // ---------- try to determine file type from filename ----------
2079 // check for typical filename of a Supaplex level package file
2080 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2081 return LEVEL_FILE_TYPE_SP;
2083 // check for typical filename of a Diamond Caves II level package file
2084 if (strSuffixLower(basename, ".dc") ||
2085 strSuffixLower(basename, ".dc2"))
2086 return LEVEL_FILE_TYPE_DC;
2088 // check for typical filename of a Sokoban level package file
2089 if (strSuffixLower(basename, ".xsb") &&
2090 strchr(basename, '%') == NULL)
2091 return LEVEL_FILE_TYPE_SB;
2093 // ---------- try to determine file type from filesize ----------
2095 checked_free(filename);
2096 filename = getPath2(getCurrentLevelDir(), basename);
2098 if (stat(filename, &file_status) == 0)
2100 // check for typical filesize of a Supaplex level package file
2101 if (file_status.st_size == 170496)
2102 return LEVEL_FILE_TYPE_SP;
2105 return LEVEL_FILE_TYPE_UNKNOWN;
2108 static int getFileTypeFromMagicBytes(char *filename, int type)
2112 if ((file = openFile(filename, MODE_READ)))
2114 char chunk_name[CHUNK_ID_LEN + 1];
2116 getFileChunkBE(file, chunk_name, NULL);
2118 if (strEqual(chunk_name, "MMII") ||
2119 strEqual(chunk_name, "MIRR"))
2120 type = LEVEL_FILE_TYPE_MM;
2128 static boolean checkForPackageFromBasename(char *basename)
2130 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2131 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2133 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2136 static char *getSingleLevelBasenameExt(int nr, char *extension)
2138 static char basename[MAX_FILENAME_LEN];
2141 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2143 sprintf(basename, "%03d.%s", nr, extension);
2148 static char *getSingleLevelBasename(int nr)
2150 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2153 static char *getPackedLevelBasename(int type)
2155 static char basename[MAX_FILENAME_LEN];
2156 char *directory = getCurrentLevelDir();
2158 DirectoryEntry *dir_entry;
2160 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2162 if ((dir = openDirectory(directory)) == NULL)
2164 Warn("cannot read current level directory '%s'", directory);
2169 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2171 char *entry_basename = dir_entry->basename;
2172 int entry_type = getFileTypeFromBasename(entry_basename);
2174 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2176 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2179 strcpy(basename, entry_basename);
2186 closeDirectory(dir);
2191 static char *getSingleLevelFilename(int nr)
2193 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2196 #if ENABLE_UNUSED_CODE
2197 static char *getPackedLevelFilename(int type)
2199 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2203 char *getDefaultLevelFilename(int nr)
2205 return getSingleLevelFilename(nr);
2208 #if ENABLE_UNUSED_CODE
2209 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2213 lfi->packed = FALSE;
2215 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2216 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2220 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2221 int type, char *format, ...)
2223 static char basename[MAX_FILENAME_LEN];
2226 va_start(ap, format);
2227 vsprintf(basename, format, ap);
2231 lfi->packed = FALSE;
2233 setString(&lfi->basename, basename);
2234 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2237 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2243 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2244 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2247 static int getFiletypeFromID(char *filetype_id)
2249 char *filetype_id_lower;
2250 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2253 if (filetype_id == NULL)
2254 return LEVEL_FILE_TYPE_UNKNOWN;
2256 filetype_id_lower = getStringToLower(filetype_id);
2258 for (i = 0; filetype_id_list[i].id != NULL; i++)
2260 char *id_lower = getStringToLower(filetype_id_list[i].id);
2262 if (strEqual(filetype_id_lower, id_lower))
2263 filetype = filetype_id_list[i].filetype;
2267 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2271 free(filetype_id_lower);
2276 char *getLocalLevelTemplateFilename(void)
2278 return getDefaultLevelFilename(-1);
2281 char *getGlobalLevelTemplateFilename(void)
2283 // global variable "leveldir_current" must be modified in the loop below
2284 LevelDirTree *leveldir_current_last = leveldir_current;
2285 char *filename = NULL;
2287 // check for template level in path from current to topmost tree node
2289 while (leveldir_current != NULL)
2291 filename = getDefaultLevelFilename(-1);
2293 if (fileExists(filename))
2296 leveldir_current = leveldir_current->node_parent;
2299 // restore global variable "leveldir_current" modified in above loop
2300 leveldir_current = leveldir_current_last;
2305 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2309 // special case: level number is negative => check for level template file
2312 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2313 getSingleLevelBasename(-1));
2315 // replace local level template filename with global template filename
2316 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2318 // no fallback if template file not existing
2322 // special case: check for file name/pattern specified in "levelinfo.conf"
2323 if (leveldir_current->level_filename != NULL)
2325 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2327 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2328 leveldir_current->level_filename, nr);
2330 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2332 if (fileExists(lfi->filename))
2335 else if (leveldir_current->level_filetype != NULL)
2337 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2339 // check for specified native level file with standard file name
2340 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2341 "%03d.%s", nr, LEVELFILE_EXTENSION);
2342 if (fileExists(lfi->filename))
2346 // check for native Rocks'n'Diamonds level file
2347 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2348 "%03d.%s", nr, LEVELFILE_EXTENSION);
2349 if (fileExists(lfi->filename))
2352 // check for Emerald Mine level file (V1)
2353 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2354 'a' + (nr / 10) % 26, '0' + nr % 10);
2355 if (fileExists(lfi->filename))
2357 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2358 'A' + (nr / 10) % 26, '0' + nr % 10);
2359 if (fileExists(lfi->filename))
2362 // check for Emerald Mine level file (V2 to V5)
2363 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2364 if (fileExists(lfi->filename))
2367 // check for Emerald Mine level file (V6 / single mode)
2368 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2369 if (fileExists(lfi->filename))
2371 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2372 if (fileExists(lfi->filename))
2375 // check for Emerald Mine level file (V6 / teamwork mode)
2376 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2377 if (fileExists(lfi->filename))
2379 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2380 if (fileExists(lfi->filename))
2383 // check for various packed level file formats
2384 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2385 if (fileExists(lfi->filename))
2388 // no known level file found -- use default values (and fail later)
2389 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2390 "%03d.%s", nr, LEVELFILE_EXTENSION);
2393 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2395 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2396 lfi->type = getFileTypeFromBasename(lfi->basename);
2398 if (lfi->type == LEVEL_FILE_TYPE_RND)
2399 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2402 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2404 // always start with reliable default values
2405 setFileInfoToDefaults(level_file_info);
2407 level_file_info->nr = nr; // set requested level number
2409 determineLevelFileInfo_Filename(level_file_info);
2410 determineLevelFileInfo_Filetype(level_file_info);
2413 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2414 struct LevelFileInfo *lfi_to)
2416 lfi_to->nr = lfi_from->nr;
2417 lfi_to->type = lfi_from->type;
2418 lfi_to->packed = lfi_from->packed;
2420 setString(&lfi_to->basename, lfi_from->basename);
2421 setString(&lfi_to->filename, lfi_from->filename);
2424 // ----------------------------------------------------------------------------
2425 // functions for loading R'n'D level
2426 // ----------------------------------------------------------------------------
2428 int getMappedElement(int element)
2430 // remap some (historic, now obsolete) elements
2434 case EL_PLAYER_OBSOLETE:
2435 element = EL_PLAYER_1;
2438 case EL_KEY_OBSOLETE:
2442 case EL_EM_KEY_1_FILE_OBSOLETE:
2443 element = EL_EM_KEY_1;
2446 case EL_EM_KEY_2_FILE_OBSOLETE:
2447 element = EL_EM_KEY_2;
2450 case EL_EM_KEY_3_FILE_OBSOLETE:
2451 element = EL_EM_KEY_3;
2454 case EL_EM_KEY_4_FILE_OBSOLETE:
2455 element = EL_EM_KEY_4;
2458 case EL_ENVELOPE_OBSOLETE:
2459 element = EL_ENVELOPE_1;
2467 if (element >= NUM_FILE_ELEMENTS)
2469 Warn("invalid level element %d", element);
2471 element = EL_UNKNOWN;
2479 static int getMappedElementByVersion(int element, int game_version)
2481 // remap some elements due to certain game version
2483 if (game_version <= VERSION_IDENT(2,2,0,0))
2485 // map game font elements
2486 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2487 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2488 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2489 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2492 if (game_version < VERSION_IDENT(3,0,0,0))
2494 // map Supaplex gravity tube elements
2495 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2496 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2497 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2498 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2505 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2507 level->file_version = getFileVersion(file);
2508 level->game_version = getFileVersion(file);
2513 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2515 level->creation_date.year = getFile16BitBE(file);
2516 level->creation_date.month = getFile8Bit(file);
2517 level->creation_date.day = getFile8Bit(file);
2519 level->creation_date.src = DATE_SRC_LEVELFILE;
2524 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2526 int initial_player_stepsize;
2527 int initial_player_gravity;
2530 level->fieldx = getFile8Bit(file);
2531 level->fieldy = getFile8Bit(file);
2533 level->time = getFile16BitBE(file);
2534 level->gems_needed = getFile16BitBE(file);
2536 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2537 level->name[i] = getFile8Bit(file);
2538 level->name[MAX_LEVEL_NAME_LEN] = 0;
2540 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2541 level->score[i] = getFile8Bit(file);
2543 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2544 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2545 for (y = 0; y < 3; y++)
2546 for (x = 0; x < 3; x++)
2547 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2549 level->amoeba_speed = getFile8Bit(file);
2550 level->time_magic_wall = getFile8Bit(file);
2551 level->time_wheel = getFile8Bit(file);
2552 level->amoeba_content = getMappedElement(getFile8Bit(file));
2554 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2557 for (i = 0; i < MAX_PLAYERS; i++)
2558 level->initial_player_stepsize[i] = initial_player_stepsize;
2560 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2562 for (i = 0; i < MAX_PLAYERS; i++)
2563 level->initial_player_gravity[i] = initial_player_gravity;
2565 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2566 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2568 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2570 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2571 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2572 level->can_move_into_acid_bits = getFile32BitBE(file);
2573 level->dont_collide_with_bits = getFile8Bit(file);
2575 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2576 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2578 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2579 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2580 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2582 level->game_engine_type = getFile8Bit(file);
2584 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2589 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2593 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2594 level->name[i] = getFile8Bit(file);
2595 level->name[MAX_LEVEL_NAME_LEN] = 0;
2600 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2604 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2605 level->author[i] = getFile8Bit(file);
2606 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2611 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2614 int chunk_size_expected = level->fieldx * level->fieldy;
2616 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2617 stored with 16-bit encoding (and should be twice as big then).
2618 Even worse, playfield data was stored 16-bit when only yamyam content
2619 contained 16-bit elements and vice versa. */
2621 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2622 chunk_size_expected *= 2;
2624 if (chunk_size_expected != chunk_size)
2626 ReadUnusedBytesFromFile(file, chunk_size);
2627 return chunk_size_expected;
2630 for (y = 0; y < level->fieldy; y++)
2631 for (x = 0; x < level->fieldx; x++)
2632 level->field[x][y] =
2633 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2638 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2641 int header_size = 4;
2642 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2643 int chunk_size_expected = header_size + content_size;
2645 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2646 stored with 16-bit encoding (and should be twice as big then).
2647 Even worse, playfield data was stored 16-bit when only yamyam content
2648 contained 16-bit elements and vice versa. */
2650 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2651 chunk_size_expected += content_size;
2653 if (chunk_size_expected != chunk_size)
2655 ReadUnusedBytesFromFile(file, chunk_size);
2656 return chunk_size_expected;
2660 level->num_yamyam_contents = getFile8Bit(file);
2664 // correct invalid number of content fields -- should never happen
2665 if (level->num_yamyam_contents < 1 ||
2666 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2667 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2669 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2670 for (y = 0; y < 3; y++)
2671 for (x = 0; x < 3; x++)
2672 level->yamyam_content[i].e[x][y] =
2673 getMappedElement(level->encoding_16bit_field ?
2674 getFile16BitBE(file) : getFile8Bit(file));
2678 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2683 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2685 element = getMappedElement(getFile16BitBE(file));
2686 num_contents = getFile8Bit(file);
2688 getFile8Bit(file); // content x size (unused)
2689 getFile8Bit(file); // content y size (unused)
2691 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2693 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2694 for (y = 0; y < 3; y++)
2695 for (x = 0; x < 3; x++)
2696 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2698 // correct invalid number of content fields -- should never happen
2699 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2700 num_contents = STD_ELEMENT_CONTENTS;
2702 if (element == EL_YAMYAM)
2704 level->num_yamyam_contents = num_contents;
2706 for (i = 0; i < num_contents; i++)
2707 for (y = 0; y < 3; y++)
2708 for (x = 0; x < 3; x++)
2709 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2711 else if (element == EL_BD_AMOEBA)
2713 level->amoeba_content = content_array[0][0][0];
2717 Warn("cannot load content for element '%d'", element);
2723 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2729 int chunk_size_expected;
2731 element = getMappedElement(getFile16BitBE(file));
2732 if (!IS_ENVELOPE(element))
2733 element = EL_ENVELOPE_1;
2735 envelope_nr = element - EL_ENVELOPE_1;
2737 envelope_len = getFile16BitBE(file);
2739 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2740 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2742 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2744 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2745 if (chunk_size_expected != chunk_size)
2747 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2748 return chunk_size_expected;
2751 for (i = 0; i < envelope_len; i++)
2752 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2757 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2759 int num_changed_custom_elements = getFile16BitBE(file);
2760 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2763 if (chunk_size_expected != chunk_size)
2765 ReadUnusedBytesFromFile(file, chunk_size - 2);
2766 return chunk_size_expected;
2769 for (i = 0; i < num_changed_custom_elements; i++)
2771 int element = getMappedElement(getFile16BitBE(file));
2772 int properties = getFile32BitBE(file);
2774 if (IS_CUSTOM_ELEMENT(element))
2775 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2777 Warn("invalid custom element number %d", element);
2779 // older game versions that wrote level files with CUS1 chunks used
2780 // different default push delay values (not yet stored in level file)
2781 element_info[element].push_delay_fixed = 2;
2782 element_info[element].push_delay_random = 8;
2785 level->file_has_custom_elements = TRUE;
2790 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2792 int num_changed_custom_elements = getFile16BitBE(file);
2793 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2796 if (chunk_size_expected != chunk_size)
2798 ReadUnusedBytesFromFile(file, chunk_size - 2);
2799 return chunk_size_expected;
2802 for (i = 0; i < num_changed_custom_elements; i++)
2804 int element = getMappedElement(getFile16BitBE(file));
2805 int custom_target_element = getMappedElement(getFile16BitBE(file));
2807 if (IS_CUSTOM_ELEMENT(element))
2808 element_info[element].change->target_element = custom_target_element;
2810 Warn("invalid custom element number %d", element);
2813 level->file_has_custom_elements = TRUE;
2818 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2820 int num_changed_custom_elements = getFile16BitBE(file);
2821 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2824 if (chunk_size_expected != chunk_size)
2826 ReadUnusedBytesFromFile(file, chunk_size - 2);
2827 return chunk_size_expected;
2830 for (i = 0; i < num_changed_custom_elements; i++)
2832 int element = getMappedElement(getFile16BitBE(file));
2833 struct ElementInfo *ei = &element_info[element];
2834 unsigned int event_bits;
2836 if (!IS_CUSTOM_ELEMENT(element))
2838 Warn("invalid custom element number %d", element);
2840 element = EL_INTERNAL_DUMMY;
2843 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2844 ei->description[j] = getFile8Bit(file);
2845 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2847 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2849 // some free bytes for future properties and padding
2850 ReadUnusedBytesFromFile(file, 7);
2852 ei->use_gfx_element = getFile8Bit(file);
2853 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2855 ei->collect_score_initial = getFile8Bit(file);
2856 ei->collect_count_initial = getFile8Bit(file);
2858 ei->push_delay_fixed = getFile16BitBE(file);
2859 ei->push_delay_random = getFile16BitBE(file);
2860 ei->move_delay_fixed = getFile16BitBE(file);
2861 ei->move_delay_random = getFile16BitBE(file);
2863 ei->move_pattern = getFile16BitBE(file);
2864 ei->move_direction_initial = getFile8Bit(file);
2865 ei->move_stepsize = getFile8Bit(file);
2867 for (y = 0; y < 3; y++)
2868 for (x = 0; x < 3; x++)
2869 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2871 // bits 0 - 31 of "has_event[]"
2872 event_bits = getFile32BitBE(file);
2873 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2874 if (event_bits & (1u << j))
2875 ei->change->has_event[j] = TRUE;
2877 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2879 ei->change->delay_fixed = getFile16BitBE(file);
2880 ei->change->delay_random = getFile16BitBE(file);
2881 ei->change->delay_frames = getFile16BitBE(file);
2883 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2885 ei->change->explode = getFile8Bit(file);
2886 ei->change->use_target_content = getFile8Bit(file);
2887 ei->change->only_if_complete = getFile8Bit(file);
2888 ei->change->use_random_replace = getFile8Bit(file);
2890 ei->change->random_percentage = getFile8Bit(file);
2891 ei->change->replace_when = getFile8Bit(file);
2893 for (y = 0; y < 3; y++)
2894 for (x = 0; x < 3; x++)
2895 ei->change->target_content.e[x][y] =
2896 getMappedElement(getFile16BitBE(file));
2898 ei->slippery_type = getFile8Bit(file);
2900 // some free bytes for future properties and padding
2901 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2903 // mark that this custom element has been modified
2904 ei->modified_settings = TRUE;
2907 level->file_has_custom_elements = TRUE;
2912 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2914 struct ElementInfo *ei;
2915 int chunk_size_expected;
2919 // ---------- custom element base property values (96 bytes) ----------------
2921 element = getMappedElement(getFile16BitBE(file));
2923 if (!IS_CUSTOM_ELEMENT(element))
2925 Warn("invalid custom element number %d", element);
2927 ReadUnusedBytesFromFile(file, chunk_size - 2);
2932 ei = &element_info[element];
2934 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2935 ei->description[i] = getFile8Bit(file);
2936 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2938 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2940 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2942 ei->num_change_pages = getFile8Bit(file);
2944 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2945 if (chunk_size_expected != chunk_size)
2947 ReadUnusedBytesFromFile(file, chunk_size - 43);
2948 return chunk_size_expected;
2951 ei->ce_value_fixed_initial = getFile16BitBE(file);
2952 ei->ce_value_random_initial = getFile16BitBE(file);
2953 ei->use_last_ce_value = getFile8Bit(file);
2955 ei->use_gfx_element = getFile8Bit(file);
2956 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2958 ei->collect_score_initial = getFile8Bit(file);
2959 ei->collect_count_initial = getFile8Bit(file);
2961 ei->drop_delay_fixed = getFile8Bit(file);
2962 ei->push_delay_fixed = getFile8Bit(file);
2963 ei->drop_delay_random = getFile8Bit(file);
2964 ei->push_delay_random = getFile8Bit(file);
2965 ei->move_delay_fixed = getFile16BitBE(file);
2966 ei->move_delay_random = getFile16BitBE(file);
2968 // bits 0 - 15 of "move_pattern" ...
2969 ei->move_pattern = getFile16BitBE(file);
2970 ei->move_direction_initial = getFile8Bit(file);
2971 ei->move_stepsize = getFile8Bit(file);
2973 ei->slippery_type = getFile8Bit(file);
2975 for (y = 0; y < 3; y++)
2976 for (x = 0; x < 3; x++)
2977 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2979 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2980 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2981 ei->move_leave_type = getFile8Bit(file);
2983 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2984 ei->move_pattern |= (getFile16BitBE(file) << 16);
2986 ei->access_direction = getFile8Bit(file);
2988 ei->explosion_delay = getFile8Bit(file);
2989 ei->ignition_delay = getFile8Bit(file);
2990 ei->explosion_type = getFile8Bit(file);
2992 // some free bytes for future custom property values and padding
2993 ReadUnusedBytesFromFile(file, 1);
2995 // ---------- change page property values (48 bytes) ------------------------
2997 setElementChangePages(ei, ei->num_change_pages);
2999 for (i = 0; i < ei->num_change_pages; i++)
3001 struct ElementChangeInfo *change = &ei->change_page[i];
3002 unsigned int event_bits;
3004 // always start with reliable default values
3005 setElementChangeInfoToDefaults(change);
3007 // bits 0 - 31 of "has_event[]" ...
3008 event_bits = getFile32BitBE(file);
3009 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3010 if (event_bits & (1u << j))
3011 change->has_event[j] = TRUE;
3013 change->target_element = getMappedElement(getFile16BitBE(file));
3015 change->delay_fixed = getFile16BitBE(file);
3016 change->delay_random = getFile16BitBE(file);
3017 change->delay_frames = getFile16BitBE(file);
3019 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3021 change->explode = getFile8Bit(file);
3022 change->use_target_content = getFile8Bit(file);
3023 change->only_if_complete = getFile8Bit(file);
3024 change->use_random_replace = getFile8Bit(file);
3026 change->random_percentage = getFile8Bit(file);
3027 change->replace_when = getFile8Bit(file);
3029 for (y = 0; y < 3; y++)
3030 for (x = 0; x < 3; x++)
3031 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3033 change->can_change = getFile8Bit(file);
3035 change->trigger_side = getFile8Bit(file);
3037 change->trigger_player = getFile8Bit(file);
3038 change->trigger_page = getFile8Bit(file);
3040 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3041 CH_PAGE_ANY : (1 << change->trigger_page));
3043 change->has_action = getFile8Bit(file);
3044 change->action_type = getFile8Bit(file);
3045 change->action_mode = getFile8Bit(file);
3046 change->action_arg = getFile16BitBE(file);
3048 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3049 event_bits = getFile8Bit(file);
3050 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3051 if (event_bits & (1u << (j - 32)))
3052 change->has_event[j] = TRUE;
3055 // mark this custom element as modified
3056 ei->modified_settings = TRUE;
3058 level->file_has_custom_elements = TRUE;
3063 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3065 struct ElementInfo *ei;
3066 struct ElementGroupInfo *group;
3070 element = getMappedElement(getFile16BitBE(file));
3072 if (!IS_GROUP_ELEMENT(element))
3074 Warn("invalid group element number %d", element);
3076 ReadUnusedBytesFromFile(file, chunk_size - 2);
3081 ei = &element_info[element];
3083 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3084 ei->description[i] = getFile8Bit(file);
3085 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3087 group = element_info[element].group;
3089 group->num_elements = getFile8Bit(file);
3091 ei->use_gfx_element = getFile8Bit(file);
3092 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3094 group->choice_mode = getFile8Bit(file);
3096 // some free bytes for future values and padding
3097 ReadUnusedBytesFromFile(file, 3);
3099 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3100 group->element[i] = getMappedElement(getFile16BitBE(file));
3102 // mark this group element as modified
3103 element_info[element].modified_settings = TRUE;
3105 level->file_has_custom_elements = TRUE;
3110 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3111 int element, int real_element)
3113 int micro_chunk_size = 0;
3114 int conf_type = getFile8Bit(file);
3115 int byte_mask = conf_type & CONF_MASK_BYTES;
3116 boolean element_found = FALSE;
3119 micro_chunk_size += 1;
3121 if (byte_mask == CONF_MASK_MULTI_BYTES)
3123 int num_bytes = getFile16BitBE(file);
3124 byte *buffer = checked_malloc(num_bytes);
3126 ReadBytesFromFile(file, buffer, num_bytes);
3128 for (i = 0; conf[i].data_type != -1; i++)
3130 if (conf[i].element == element &&
3131 conf[i].conf_type == conf_type)
3133 int data_type = conf[i].data_type;
3134 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3135 int max_num_entities = conf[i].max_num_entities;
3137 if (num_entities > max_num_entities)
3139 Warn("truncating number of entities for element %d from %d to %d",
3140 element, num_entities, max_num_entities);
3142 num_entities = max_num_entities;
3145 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3146 data_type == TYPE_CONTENT_LIST))
3148 // for element and content lists, zero entities are not allowed
3149 Warn("found empty list of entities for element %d", element);
3151 // do not set "num_entities" here to prevent reading behind buffer
3153 *(int *)(conf[i].num_entities) = 1; // at least one is required
3157 *(int *)(conf[i].num_entities) = num_entities;
3160 element_found = TRUE;
3162 if (data_type == TYPE_STRING)
3164 char *string = (char *)(conf[i].value);
3167 for (j = 0; j < max_num_entities; j++)
3168 string[j] = (j < num_entities ? buffer[j] : '\0');
3170 else if (data_type == TYPE_ELEMENT_LIST)
3172 int *element_array = (int *)(conf[i].value);
3175 for (j = 0; j < num_entities; j++)
3177 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3179 else if (data_type == TYPE_CONTENT_LIST)
3181 struct Content *content= (struct Content *)(conf[i].value);
3184 for (c = 0; c < num_entities; c++)
3185 for (y = 0; y < 3; y++)
3186 for (x = 0; x < 3; x++)
3187 content[c].e[x][y] =
3188 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3191 element_found = FALSE;
3197 checked_free(buffer);
3199 micro_chunk_size += 2 + num_bytes;
3201 else // constant size configuration data (1, 2 or 4 bytes)
3203 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3204 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3205 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3207 for (i = 0; conf[i].data_type != -1; i++)
3209 if (conf[i].element == element &&
3210 conf[i].conf_type == conf_type)
3212 int data_type = conf[i].data_type;
3214 if (data_type == TYPE_ELEMENT)
3215 value = getMappedElement(value);
3217 if (data_type == TYPE_BOOLEAN)
3218 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3220 *(int *) (conf[i].value) = value;
3222 element_found = TRUE;
3228 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3233 char *error_conf_chunk_bytes =
3234 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3235 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3236 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3237 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3238 int error_element = real_element;
3240 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3241 error_conf_chunk_bytes, error_conf_chunk_token,
3242 error_element, EL_NAME(error_element));
3245 return micro_chunk_size;
3248 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3250 int real_chunk_size = 0;
3252 li = *level; // copy level data into temporary buffer
3254 while (!checkEndOfFile(file))
3256 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3258 if (real_chunk_size >= chunk_size)
3262 *level = li; // copy temporary buffer back to level data
3264 return real_chunk_size;
3267 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3269 int real_chunk_size = 0;
3271 li = *level; // copy level data into temporary buffer
3273 while (!checkEndOfFile(file))
3275 int element = getMappedElement(getFile16BitBE(file));
3277 real_chunk_size += 2;
3278 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3280 if (real_chunk_size >= chunk_size)
3284 *level = li; // copy temporary buffer back to level data
3286 return real_chunk_size;
3289 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3291 int real_chunk_size = 0;
3293 li = *level; // copy level data into temporary buffer
3295 while (!checkEndOfFile(file))
3297 int element = getMappedElement(getFile16BitBE(file));
3299 real_chunk_size += 2;
3300 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3302 if (real_chunk_size >= chunk_size)
3306 *level = li; // copy temporary buffer back to level data
3308 return real_chunk_size;
3311 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3313 int element = getMappedElement(getFile16BitBE(file));
3314 int envelope_nr = element - EL_ENVELOPE_1;
3315 int real_chunk_size = 2;
3317 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3319 while (!checkEndOfFile(file))
3321 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3324 if (real_chunk_size >= chunk_size)
3328 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3330 return real_chunk_size;
3333 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3335 int element = getMappedElement(getFile16BitBE(file));
3336 int real_chunk_size = 2;
3337 struct ElementInfo *ei = &element_info[element];
3340 xx_ei = *ei; // copy element data into temporary buffer
3342 xx_ei.num_change_pages = -1;
3344 while (!checkEndOfFile(file))
3346 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3348 if (xx_ei.num_change_pages != -1)
3351 if (real_chunk_size >= chunk_size)
3357 if (ei->num_change_pages == -1)
3359 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3362 ei->num_change_pages = 1;
3364 setElementChangePages(ei, 1);
3365 setElementChangeInfoToDefaults(ei->change);
3367 return real_chunk_size;
3370 // initialize number of change pages stored for this custom element
3371 setElementChangePages(ei, ei->num_change_pages);
3372 for (i = 0; i < ei->num_change_pages; i++)
3373 setElementChangeInfoToDefaults(&ei->change_page[i]);
3375 // start with reading properties for the first change page
3376 xx_current_change_page = 0;
3378 while (!checkEndOfFile(file))
3380 // level file might contain invalid change page number
3381 if (xx_current_change_page >= ei->num_change_pages)
3384 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3386 xx_change = *change; // copy change data into temporary buffer
3388 resetEventBits(); // reset bits; change page might have changed
3390 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3393 *change = xx_change;
3395 setEventFlagsFromEventBits(change);
3397 if (real_chunk_size >= chunk_size)
3401 level->file_has_custom_elements = TRUE;
3403 return real_chunk_size;
3406 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3408 int element = getMappedElement(getFile16BitBE(file));
3409 int real_chunk_size = 2;
3410 struct ElementInfo *ei = &element_info[element];
3411 struct ElementGroupInfo *group = ei->group;
3416 xx_ei = *ei; // copy element data into temporary buffer
3417 xx_group = *group; // copy group data into temporary buffer
3419 while (!checkEndOfFile(file))
3421 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3424 if (real_chunk_size >= chunk_size)
3431 level->file_has_custom_elements = TRUE;
3433 return real_chunk_size;
3436 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3438 int element = getMappedElement(getFile16BitBE(file));
3439 int real_chunk_size = 2;
3440 struct ElementInfo *ei = &element_info[element];
3442 xx_ei = *ei; // copy element data into temporary buffer
3444 while (!checkEndOfFile(file))
3446 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3449 if (real_chunk_size >= chunk_size)
3455 level->file_has_custom_elements = TRUE;
3457 return real_chunk_size;
3460 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3461 struct LevelFileInfo *level_file_info,
3462 boolean level_info_only)
3464 char *filename = level_file_info->filename;
3465 char cookie[MAX_LINE_LEN];
3466 char chunk_name[CHUNK_ID_LEN + 1];
3470 if (!(file = openFile(filename, MODE_READ)))
3472 level->no_valid_file = TRUE;
3473 level->no_level_file = TRUE;
3475 if (level_info_only)
3478 Warn("cannot read level '%s' -- using empty level", filename);
3480 if (!setup.editor.use_template_for_new_levels)
3483 // if level file not found, try to initialize level data from template
3484 filename = getGlobalLevelTemplateFilename();
3486 if (!(file = openFile(filename, MODE_READ)))
3489 // default: for empty levels, use level template for custom elements
3490 level->use_custom_template = TRUE;
3492 level->no_valid_file = FALSE;
3495 getFileChunkBE(file, chunk_name, NULL);
3496 if (strEqual(chunk_name, "RND1"))
3498 getFile32BitBE(file); // not used
3500 getFileChunkBE(file, chunk_name, NULL);
3501 if (!strEqual(chunk_name, "CAVE"))
3503 level->no_valid_file = TRUE;
3505 Warn("unknown format of level file '%s'", filename);
3512 else // check for pre-2.0 file format with cookie string
3514 strcpy(cookie, chunk_name);
3515 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3517 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3518 cookie[strlen(cookie) - 1] = '\0';
3520 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3522 level->no_valid_file = TRUE;
3524 Warn("unknown format of level file '%s'", filename);
3531 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3533 level->no_valid_file = TRUE;
3535 Warn("unsupported version of level file '%s'", filename);
3542 // pre-2.0 level files have no game version, so use file version here
3543 level->game_version = level->file_version;
3546 if (level->file_version < FILE_VERSION_1_2)
3548 // level files from versions before 1.2.0 without chunk structure
3549 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3550 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3558 int (*loader)(File *, int, struct LevelInfo *);
3562 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3563 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3564 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3565 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3566 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3567 { "INFO", -1, LoadLevel_INFO },
3568 { "BODY", -1, LoadLevel_BODY },
3569 { "CONT", -1, LoadLevel_CONT },
3570 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3571 { "CNT3", -1, LoadLevel_CNT3 },
3572 { "CUS1", -1, LoadLevel_CUS1 },
3573 { "CUS2", -1, LoadLevel_CUS2 },
3574 { "CUS3", -1, LoadLevel_CUS3 },
3575 { "CUS4", -1, LoadLevel_CUS4 },
3576 { "GRP1", -1, LoadLevel_GRP1 },
3577 { "CONF", -1, LoadLevel_CONF },
3578 { "ELEM", -1, LoadLevel_ELEM },
3579 { "NOTE", -1, LoadLevel_NOTE },
3580 { "CUSX", -1, LoadLevel_CUSX },
3581 { "GRPX", -1, LoadLevel_GRPX },
3582 { "EMPX", -1, LoadLevel_EMPX },
3587 while (getFileChunkBE(file, chunk_name, &chunk_size))
3591 while (chunk_info[i].name != NULL &&
3592 !strEqual(chunk_name, chunk_info[i].name))
3595 if (chunk_info[i].name == NULL)
3597 Warn("unknown chunk '%s' in level file '%s'",
3598 chunk_name, filename);
3600 ReadUnusedBytesFromFile(file, chunk_size);
3602 else if (chunk_info[i].size != -1 &&
3603 chunk_info[i].size != chunk_size)
3605 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3606 chunk_size, chunk_name, filename);
3608 ReadUnusedBytesFromFile(file, chunk_size);
3612 // call function to load this level chunk
3613 int chunk_size_expected =
3614 (chunk_info[i].loader)(file, chunk_size, level);
3616 if (chunk_size_expected < 0)
3618 Warn("error reading chunk '%s' in level file '%s'",
3619 chunk_name, filename);
3624 // the size of some chunks cannot be checked before reading other
3625 // chunks first (like "HEAD" and "BODY") that contain some header
3626 // information, so check them here
3627 if (chunk_size_expected != chunk_size)
3629 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3630 chunk_size, chunk_name, filename);
3642 // ----------------------------------------------------------------------------
3643 // functions for loading EM level
3644 // ----------------------------------------------------------------------------
3646 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3648 static int ball_xy[8][2] =
3659 struct LevelInfo_EM *level_em = level->native_em_level;
3660 struct CAVE *cav = level_em->cav;
3663 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3664 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3666 cav->time_seconds = level->time;
3667 cav->gems_needed = level->gems_needed;
3669 cav->emerald_score = level->score[SC_EMERALD];
3670 cav->diamond_score = level->score[SC_DIAMOND];
3671 cav->alien_score = level->score[SC_ROBOT];
3672 cav->tank_score = level->score[SC_SPACESHIP];
3673 cav->bug_score = level->score[SC_BUG];
3674 cav->eater_score = level->score[SC_YAMYAM];
3675 cav->nut_score = level->score[SC_NUT];
3676 cav->dynamite_score = level->score[SC_DYNAMITE];
3677 cav->key_score = level->score[SC_KEY];
3678 cav->exit_score = level->score[SC_TIME_BONUS];
3680 cav->num_eater_arrays = level->num_yamyam_contents;
3682 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3683 for (y = 0; y < 3; y++)
3684 for (x = 0; x < 3; x++)
3685 cav->eater_array[i][y * 3 + x] =
3686 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3688 cav->amoeba_time = level->amoeba_speed;
3689 cav->wonderwall_time = level->time_magic_wall;
3690 cav->wheel_time = level->time_wheel;
3692 cav->android_move_time = level->android_move_time;
3693 cav->android_clone_time = level->android_clone_time;
3694 cav->ball_random = level->ball_random;
3695 cav->ball_active = level->ball_active_initial;
3696 cav->ball_time = level->ball_time;
3697 cav->num_ball_arrays = level->num_ball_contents;
3699 cav->lenses_score = level->lenses_score;
3700 cav->magnify_score = level->magnify_score;
3701 cav->slurp_score = level->slurp_score;
3703 cav->lenses_time = level->lenses_time;
3704 cav->magnify_time = level->magnify_time;
3706 cav->wind_direction =
3707 map_direction_RND_to_EM(level->wind_direction_initial);
3709 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3710 for (j = 0; j < 8; j++)
3711 cav->ball_array[i][j] =
3712 map_element_RND_to_EM_cave(level->ball_content[i].
3713 e[ball_xy[j][0]][ball_xy[j][1]]);
3715 map_android_clone_elements_RND_to_EM(level);
3717 // first fill the complete playfield with the empty space element
3718 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3719 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3720 cav->cave[x][y] = Cblank;
3722 // then copy the real level contents from level file into the playfield
3723 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3725 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3727 if (level->field[x][y] == EL_AMOEBA_DEAD)
3728 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3730 cav->cave[x][y] = new_element;
3733 for (i = 0; i < MAX_PLAYERS; i++)
3735 cav->player_x[i] = -1;
3736 cav->player_y[i] = -1;
3739 // initialize player positions and delete players from the playfield
3740 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3742 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3744 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3746 cav->player_x[player_nr] = x;
3747 cav->player_y[player_nr] = y;
3749 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3754 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3756 static int ball_xy[8][2] =
3767 struct LevelInfo_EM *level_em = level->native_em_level;
3768 struct CAVE *cav = level_em->cav;
3771 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3772 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3774 level->time = cav->time_seconds;
3775 level->gems_needed = cav->gems_needed;
3777 sprintf(level->name, "Level %d", level->file_info.nr);
3779 level->score[SC_EMERALD] = cav->emerald_score;
3780 level->score[SC_DIAMOND] = cav->diamond_score;
3781 level->score[SC_ROBOT] = cav->alien_score;
3782 level->score[SC_SPACESHIP] = cav->tank_score;
3783 level->score[SC_BUG] = cav->bug_score;
3784 level->score[SC_YAMYAM] = cav->eater_score;
3785 level->score[SC_NUT] = cav->nut_score;
3786 level->score[SC_DYNAMITE] = cav->dynamite_score;
3787 level->score[SC_KEY] = cav->key_score;
3788 level->score[SC_TIME_BONUS] = cav->exit_score;
3790 level->num_yamyam_contents = cav->num_eater_arrays;
3792 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3793 for (y = 0; y < 3; y++)
3794 for (x = 0; x < 3; x++)
3795 level->yamyam_content[i].e[x][y] =
3796 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3798 level->amoeba_speed = cav->amoeba_time;
3799 level->time_magic_wall = cav->wonderwall_time;
3800 level->time_wheel = cav->wheel_time;
3802 level->android_move_time = cav->android_move_time;
3803 level->android_clone_time = cav->android_clone_time;
3804 level->ball_random = cav->ball_random;
3805 level->ball_active_initial = cav->ball_active;
3806 level->ball_time = cav->ball_time;
3807 level->num_ball_contents = cav->num_ball_arrays;
3809 level->lenses_score = cav->lenses_score;
3810 level->magnify_score = cav->magnify_score;
3811 level->slurp_score = cav->slurp_score;
3813 level->lenses_time = cav->lenses_time;
3814 level->magnify_time = cav->magnify_time;
3816 level->wind_direction_initial =
3817 map_direction_EM_to_RND(cav->wind_direction);
3819 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3820 for (j = 0; j < 8; j++)
3821 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3822 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3824 map_android_clone_elements_EM_to_RND(level);
3826 // convert the playfield (some elements need special treatment)
3827 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3829 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3831 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3832 new_element = EL_AMOEBA_DEAD;
3834 level->field[x][y] = new_element;
3837 for (i = 0; i < MAX_PLAYERS; i++)
3839 // in case of all players set to the same field, use the first player
3840 int nr = MAX_PLAYERS - i - 1;
3841 int jx = cav->player_x[nr];
3842 int jy = cav->player_y[nr];
3844 if (jx != -1 && jy != -1)
3845 level->field[jx][jy] = EL_PLAYER_1 + nr;
3848 // time score is counted for each 10 seconds left in Emerald Mine levels
3849 level->time_score_base = 10;
3853 // ----------------------------------------------------------------------------
3854 // functions for loading SP level
3855 // ----------------------------------------------------------------------------
3857 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3859 struct LevelInfo_SP *level_sp = level->native_sp_level;
3860 LevelInfoType *header = &level_sp->header;
3863 level_sp->width = level->fieldx;
3864 level_sp->height = level->fieldy;
3866 for (x = 0; x < level->fieldx; x++)
3867 for (y = 0; y < level->fieldy; y++)
3868 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3870 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3872 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3873 header->LevelTitle[i] = level->name[i];
3874 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3876 header->InfotronsNeeded = level->gems_needed;
3878 header->SpecialPortCount = 0;
3880 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3882 boolean gravity_port_found = FALSE;
3883 boolean gravity_port_valid = FALSE;
3884 int gravity_port_flag;
3885 int gravity_port_base_element;
3886 int element = level->field[x][y];
3888 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3889 element <= EL_SP_GRAVITY_ON_PORT_UP)
3891 gravity_port_found = TRUE;
3892 gravity_port_valid = TRUE;
3893 gravity_port_flag = 1;
3894 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3896 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3897 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3899 gravity_port_found = TRUE;
3900 gravity_port_valid = TRUE;
3901 gravity_port_flag = 0;
3902 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3904 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3905 element <= EL_SP_GRAVITY_PORT_UP)
3907 // change R'n'D style gravity inverting special port to normal port
3908 // (there are no gravity inverting ports in native Supaplex engine)
3910 gravity_port_found = TRUE;
3911 gravity_port_valid = FALSE;
3912 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3915 if (gravity_port_found)
3917 if (gravity_port_valid &&
3918 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3920 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3922 port->PortLocation = (y * level->fieldx + x) * 2;
3923 port->Gravity = gravity_port_flag;
3925 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3927 header->SpecialPortCount++;
3931 // change special gravity port to normal port
3933 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3936 level_sp->playfield[x][y] = element - EL_SP_START;
3941 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3943 struct LevelInfo_SP *level_sp = level->native_sp_level;
3944 LevelInfoType *header = &level_sp->header;
3945 boolean num_invalid_elements = 0;
3948 level->fieldx = level_sp->width;
3949 level->fieldy = level_sp->height;
3951 for (x = 0; x < level->fieldx; x++)
3953 for (y = 0; y < level->fieldy; y++)
3955 int element_old = level_sp->playfield[x][y];
3956 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3958 if (element_new == EL_UNKNOWN)
3960 num_invalid_elements++;
3962 Debug("level:native:SP", "invalid element %d at position %d, %d",
3966 level->field[x][y] = element_new;
3970 if (num_invalid_elements > 0)
3971 Warn("found %d invalid elements%s", num_invalid_elements,
3972 (!options.debug ? " (use '--debug' for more details)" : ""));
3974 for (i = 0; i < MAX_PLAYERS; i++)
3975 level->initial_player_gravity[i] =
3976 (header->InitialGravity == 1 ? TRUE : FALSE);
3978 // skip leading spaces
3979 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3980 if (header->LevelTitle[i] != ' ')
3984 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3985 level->name[j] = header->LevelTitle[i];
3986 level->name[j] = '\0';
3988 // cut trailing spaces
3990 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3991 level->name[j - 1] = '\0';
3993 level->gems_needed = header->InfotronsNeeded;
3995 for (i = 0; i < header->SpecialPortCount; i++)
3997 SpecialPortType *port = &header->SpecialPort[i];
3998 int port_location = port->PortLocation;
3999 int gravity = port->Gravity;
4000 int port_x, port_y, port_element;
4002 port_x = (port_location / 2) % level->fieldx;
4003 port_y = (port_location / 2) / level->fieldx;
4005 if (port_x < 0 || port_x >= level->fieldx ||
4006 port_y < 0 || port_y >= level->fieldy)
4008 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4013 port_element = level->field[port_x][port_y];
4015 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4016 port_element > EL_SP_GRAVITY_PORT_UP)
4018 Warn("no special port at position (%d, %d)", port_x, port_y);
4023 // change previous (wrong) gravity inverting special port to either
4024 // gravity enabling special port or gravity disabling special port
4025 level->field[port_x][port_y] +=
4026 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4027 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4030 // change special gravity ports without database entries to normal ports
4031 for (x = 0; x < level->fieldx; x++)
4032 for (y = 0; y < level->fieldy; y++)
4033 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4034 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4035 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4037 level->time = 0; // no time limit
4038 level->amoeba_speed = 0;
4039 level->time_magic_wall = 0;
4040 level->time_wheel = 0;
4041 level->amoeba_content = EL_EMPTY;
4043 // original Supaplex does not use score values -- rate by playing time
4044 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4045 level->score[i] = 0;
4047 level->rate_time_over_score = TRUE;
4049 // there are no yamyams in supaplex levels
4050 for (i = 0; i < level->num_yamyam_contents; i++)
4051 for (x = 0; x < 3; x++)
4052 for (y = 0; y < 3; y++)
4053 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4056 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4058 struct LevelInfo_SP *level_sp = level->native_sp_level;
4059 struct DemoInfo_SP *demo = &level_sp->demo;
4062 // always start with reliable default values
4063 demo->is_available = FALSE;
4066 if (TAPE_IS_EMPTY(tape))
4069 demo->level_nr = tape.level_nr; // (currently not used)
4071 level_sp->header.DemoRandomSeed = tape.random_seed;
4075 for (i = 0; i < tape.length; i++)
4077 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4078 int demo_repeat = tape.pos[i].delay;
4079 int demo_entries = (demo_repeat + 15) / 16;
4081 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4083 Warn("tape truncated: size exceeds maximum SP demo size %d",
4089 for (j = 0; j < demo_repeat / 16; j++)
4090 demo->data[demo->length++] = 0xf0 | demo_action;
4092 if (demo_repeat % 16)
4093 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4096 demo->is_available = TRUE;
4099 static void setTapeInfoToDefaults(void);
4101 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4103 struct LevelInfo_SP *level_sp = level->native_sp_level;
4104 struct DemoInfo_SP *demo = &level_sp->demo;
4105 char *filename = level->file_info.filename;
4108 // always start with reliable default values
4109 setTapeInfoToDefaults();
4111 if (!demo->is_available)
4114 tape.level_nr = demo->level_nr; // (currently not used)
4115 tape.random_seed = level_sp->header.DemoRandomSeed;
4117 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4120 tape.pos[tape.counter].delay = 0;
4122 for (i = 0; i < demo->length; i++)
4124 int demo_action = demo->data[i] & 0x0f;
4125 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4126 int tape_action = map_key_SP_to_RND(demo_action);
4127 int tape_repeat = demo_repeat + 1;
4128 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4129 boolean success = 0;
4132 for (j = 0; j < tape_repeat; j++)
4133 success = TapeAddAction(action);
4137 Warn("SP demo truncated: size exceeds maximum tape size %d",
4144 TapeHaltRecording();
4148 // ----------------------------------------------------------------------------
4149 // functions for loading MM level
4150 // ----------------------------------------------------------------------------
4152 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4154 struct LevelInfo_MM *level_mm = level->native_mm_level;
4157 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4158 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4160 level_mm->time = level->time;
4161 level_mm->kettles_needed = level->gems_needed;
4162 level_mm->auto_count_kettles = level->auto_count_gems;
4164 level_mm->laser_red = level->mm_laser_red;
4165 level_mm->laser_green = level->mm_laser_green;
4166 level_mm->laser_blue = level->mm_laser_blue;
4168 strcpy(level_mm->name, level->name);
4169 strcpy(level_mm->author, level->author);
4171 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4172 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4173 level_mm->score[SC_KEY] = level->score[SC_KEY];
4174 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4175 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4177 level_mm->amoeba_speed = level->amoeba_speed;
4178 level_mm->time_fuse = level->mm_time_fuse;
4179 level_mm->time_bomb = level->mm_time_bomb;
4180 level_mm->time_ball = level->mm_time_ball;
4181 level_mm->time_block = level->mm_time_block;
4183 level_mm->num_ball_contents = level->num_mm_ball_contents;
4184 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4186 for (i = 0; i < level->num_mm_ball_contents; i++)
4187 level_mm->ball_content[i] =
4188 map_element_RND_to_MM(level->mm_ball_content[i]);
4190 for (x = 0; x < level->fieldx; x++)
4191 for (y = 0; y < level->fieldy; y++)
4193 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4196 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4198 struct LevelInfo_MM *level_mm = level->native_mm_level;
4201 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4202 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4204 level->time = level_mm->time;
4205 level->gems_needed = level_mm->kettles_needed;
4206 level->auto_count_gems = level_mm->auto_count_kettles;
4208 level->mm_laser_red = level_mm->laser_red;
4209 level->mm_laser_green = level_mm->laser_green;
4210 level->mm_laser_blue = level_mm->laser_blue;
4212 strcpy(level->name, level_mm->name);
4214 // only overwrite author from 'levelinfo.conf' if author defined in level
4215 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4216 strcpy(level->author, level_mm->author);
4218 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4219 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4220 level->score[SC_KEY] = level_mm->score[SC_KEY];
4221 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4222 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4224 level->amoeba_speed = level_mm->amoeba_speed;
4225 level->mm_time_fuse = level_mm->time_fuse;
4226 level->mm_time_bomb = level_mm->time_bomb;
4227 level->mm_time_ball = level_mm->time_ball;
4228 level->mm_time_block = level_mm->time_block;
4230 level->num_mm_ball_contents = level_mm->num_ball_contents;
4231 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4233 for (i = 0; i < level->num_mm_ball_contents; i++)
4234 level->mm_ball_content[i] =
4235 map_element_MM_to_RND(level_mm->ball_content[i]);
4237 for (x = 0; x < level->fieldx; x++)
4238 for (y = 0; y < level->fieldy; y++)
4239 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4243 // ----------------------------------------------------------------------------
4244 // functions for loading DC level
4245 // ----------------------------------------------------------------------------
4247 #define DC_LEVEL_HEADER_SIZE 344
4249 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4252 static int last_data_encoded;
4256 int diff_hi, diff_lo;
4257 int data_hi, data_lo;
4258 unsigned short data_decoded;
4262 last_data_encoded = 0;
4269 diff = data_encoded - last_data_encoded;
4270 diff_hi = diff & ~0xff;
4271 diff_lo = diff & 0xff;
4275 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4276 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4277 data_hi = data_hi & 0xff00;
4279 data_decoded = data_hi | data_lo;
4281 last_data_encoded = data_encoded;
4283 offset1 = (offset1 + 1) % 31;
4284 offset2 = offset2 & 0xff;
4286 return data_decoded;
4289 static int getMappedElement_DC(int element)
4297 // 0x0117 - 0x036e: (?)
4300 // 0x042d - 0x0684: (?)
4316 element = EL_CRYSTAL;
4319 case 0x0e77: // quicksand (boulder)
4320 element = EL_QUICKSAND_FAST_FULL;
4323 case 0x0e99: // slow quicksand (boulder)
4324 element = EL_QUICKSAND_FULL;
4328 element = EL_EM_EXIT_OPEN;
4332 element = EL_EM_EXIT_CLOSED;
4336 element = EL_EM_STEEL_EXIT_OPEN;
4340 element = EL_EM_STEEL_EXIT_CLOSED;
4343 case 0x0f4f: // dynamite (lit 1)
4344 element = EL_EM_DYNAMITE_ACTIVE;
4347 case 0x0f57: // dynamite (lit 2)
4348 element = EL_EM_DYNAMITE_ACTIVE;
4351 case 0x0f5f: // dynamite (lit 3)
4352 element = EL_EM_DYNAMITE_ACTIVE;
4355 case 0x0f67: // dynamite (lit 4)
4356 element = EL_EM_DYNAMITE_ACTIVE;
4363 element = EL_AMOEBA_WET;
4367 element = EL_AMOEBA_DROP;
4371 element = EL_DC_MAGIC_WALL;
4375 element = EL_SPACESHIP_UP;
4379 element = EL_SPACESHIP_DOWN;
4383 element = EL_SPACESHIP_LEFT;
4387 element = EL_SPACESHIP_RIGHT;
4391 element = EL_BUG_UP;
4395 element = EL_BUG_DOWN;
4399 element = EL_BUG_LEFT;
4403 element = EL_BUG_RIGHT;
4407 element = EL_MOLE_UP;
4411 element = EL_MOLE_DOWN;
4415 element = EL_MOLE_LEFT;
4419 element = EL_MOLE_RIGHT;
4427 element = EL_YAMYAM_UP;
4431 element = EL_SWITCHGATE_OPEN;
4435 element = EL_SWITCHGATE_CLOSED;
4439 element = EL_DC_SWITCHGATE_SWITCH_UP;
4443 element = EL_TIMEGATE_CLOSED;
4446 case 0x144c: // conveyor belt switch (green)
4447 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4450 case 0x144f: // conveyor belt switch (red)
4451 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4454 case 0x1452: // conveyor belt switch (blue)
4455 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4459 element = EL_CONVEYOR_BELT_3_MIDDLE;
4463 element = EL_CONVEYOR_BELT_3_LEFT;
4467 element = EL_CONVEYOR_BELT_3_RIGHT;
4471 element = EL_CONVEYOR_BELT_1_MIDDLE;
4475 element = EL_CONVEYOR_BELT_1_LEFT;
4479 element = EL_CONVEYOR_BELT_1_RIGHT;
4483 element = EL_CONVEYOR_BELT_4_MIDDLE;
4487 element = EL_CONVEYOR_BELT_4_LEFT;
4491 element = EL_CONVEYOR_BELT_4_RIGHT;
4495 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4499 element = EL_EXPANDABLE_WALL_VERTICAL;
4503 element = EL_EXPANDABLE_WALL_ANY;
4506 case 0x14ce: // growing steel wall (left/right)
4507 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4510 case 0x14df: // growing steel wall (up/down)
4511 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4514 case 0x14e8: // growing steel wall (up/down/left/right)
4515 element = EL_EXPANDABLE_STEELWALL_ANY;
4519 element = EL_SHIELD_DEADLY;
4523 element = EL_EXTRA_TIME;
4531 element = EL_EMPTY_SPACE;
4534 case 0x1578: // quicksand (empty)
4535 element = EL_QUICKSAND_FAST_EMPTY;
4538 case 0x1579: // slow quicksand (empty)
4539 element = EL_QUICKSAND_EMPTY;
4549 element = EL_EM_DYNAMITE;
4552 case 0x15a1: // key (red)
4553 element = EL_EM_KEY_1;
4556 case 0x15a2: // key (yellow)
4557 element = EL_EM_KEY_2;
4560 case 0x15a3: // key (blue)
4561 element = EL_EM_KEY_4;
4564 case 0x15a4: // key (green)
4565 element = EL_EM_KEY_3;
4568 case 0x15a5: // key (white)
4569 element = EL_DC_KEY_WHITE;
4573 element = EL_WALL_SLIPPERY;
4580 case 0x15a8: // wall (not round)
4584 case 0x15a9: // (blue)
4585 element = EL_CHAR_A;
4588 case 0x15aa: // (blue)
4589 element = EL_CHAR_B;
4592 case 0x15ab: // (blue)
4593 element = EL_CHAR_C;
4596 case 0x15ac: // (blue)
4597 element = EL_CHAR_D;
4600 case 0x15ad: // (blue)
4601 element = EL_CHAR_E;
4604 case 0x15ae: // (blue)
4605 element = EL_CHAR_F;
4608 case 0x15af: // (blue)
4609 element = EL_CHAR_G;
4612 case 0x15b0: // (blue)
4613 element = EL_CHAR_H;
4616 case 0x15b1: // (blue)
4617 element = EL_CHAR_I;
4620 case 0x15b2: // (blue)
4621 element = EL_CHAR_J;
4624 case 0x15b3: // (blue)
4625 element = EL_CHAR_K;
4628 case 0x15b4: // (blue)
4629 element = EL_CHAR_L;
4632 case 0x15b5: // (blue)
4633 element = EL_CHAR_M;
4636 case 0x15b6: // (blue)
4637 element = EL_CHAR_N;
4640 case 0x15b7: // (blue)
4641 element = EL_CHAR_O;
4644 case 0x15b8: // (blue)
4645 element = EL_CHAR_P;
4648 case 0x15b9: // (blue)
4649 element = EL_CHAR_Q;
4652 case 0x15ba: // (blue)
4653 element = EL_CHAR_R;
4656 case 0x15bb: // (blue)
4657 element = EL_CHAR_S;
4660 case 0x15bc: // (blue)
4661 element = EL_CHAR_T;
4664 case 0x15bd: // (blue)
4665 element = EL_CHAR_U;
4668 case 0x15be: // (blue)
4669 element = EL_CHAR_V;
4672 case 0x15bf: // (blue)
4673 element = EL_CHAR_W;
4676 case 0x15c0: // (blue)
4677 element = EL_CHAR_X;
4680 case 0x15c1: // (blue)
4681 element = EL_CHAR_Y;
4684 case 0x15c2: // (blue)
4685 element = EL_CHAR_Z;
4688 case 0x15c3: // (blue)
4689 element = EL_CHAR_AUMLAUT;
4692 case 0x15c4: // (blue)
4693 element = EL_CHAR_OUMLAUT;
4696 case 0x15c5: // (blue)
4697 element = EL_CHAR_UUMLAUT;
4700 case 0x15c6: // (blue)
4701 element = EL_CHAR_0;
4704 case 0x15c7: // (blue)
4705 element = EL_CHAR_1;
4708 case 0x15c8: // (blue)
4709 element = EL_CHAR_2;
4712 case 0x15c9: // (blue)
4713 element = EL_CHAR_3;
4716 case 0x15ca: // (blue)
4717 element = EL_CHAR_4;
4720 case 0x15cb: // (blue)
4721 element = EL_CHAR_5;
4724 case 0x15cc: // (blue)
4725 element = EL_CHAR_6;
4728 case 0x15cd: // (blue)
4729 element = EL_CHAR_7;
4732 case 0x15ce: // (blue)
4733 element = EL_CHAR_8;
4736 case 0x15cf: // (blue)
4737 element = EL_CHAR_9;
4740 case 0x15d0: // (blue)
4741 element = EL_CHAR_PERIOD;
4744 case 0x15d1: // (blue)
4745 element = EL_CHAR_EXCLAM;
4748 case 0x15d2: // (blue)
4749 element = EL_CHAR_COLON;
4752 case 0x15d3: // (blue)
4753 element = EL_CHAR_LESS;
4756 case 0x15d4: // (blue)
4757 element = EL_CHAR_GREATER;
4760 case 0x15d5: // (blue)
4761 element = EL_CHAR_QUESTION;
4764 case 0x15d6: // (blue)
4765 element = EL_CHAR_COPYRIGHT;
4768 case 0x15d7: // (blue)
4769 element = EL_CHAR_UP;
4772 case 0x15d8: // (blue)
4773 element = EL_CHAR_DOWN;
4776 case 0x15d9: // (blue)
4777 element = EL_CHAR_BUTTON;
4780 case 0x15da: // (blue)
4781 element = EL_CHAR_PLUS;
4784 case 0x15db: // (blue)
4785 element = EL_CHAR_MINUS;
4788 case 0x15dc: // (blue)
4789 element = EL_CHAR_APOSTROPHE;
4792 case 0x15dd: // (blue)
4793 element = EL_CHAR_PARENLEFT;
4796 case 0x15de: // (blue)
4797 element = EL_CHAR_PARENRIGHT;
4800 case 0x15df: // (green)
4801 element = EL_CHAR_A;
4804 case 0x15e0: // (green)
4805 element = EL_CHAR_B;
4808 case 0x15e1: // (green)
4809 element = EL_CHAR_C;
4812 case 0x15e2: // (green)
4813 element = EL_CHAR_D;
4816 case 0x15e3: // (green)
4817 element = EL_CHAR_E;
4820 case 0x15e4: // (green)
4821 element = EL_CHAR_F;
4824 case 0x15e5: // (green)
4825 element = EL_CHAR_G;
4828 case 0x15e6: // (green)
4829 element = EL_CHAR_H;
4832 case 0x15e7: // (green)
4833 element = EL_CHAR_I;
4836 case 0x15e8: // (green)
4837 element = EL_CHAR_J;
4840 case 0x15e9: // (green)
4841 element = EL_CHAR_K;
4844 case 0x15ea: // (green)
4845 element = EL_CHAR_L;
4848 case 0x15eb: // (green)
4849 element = EL_CHAR_M;
4852 case 0x15ec: // (green)
4853 element = EL_CHAR_N;
4856 case 0x15ed: // (green)
4857 element = EL_CHAR_O;
4860 case 0x15ee: // (green)
4861 element = EL_CHAR_P;
4864 case 0x15ef: // (green)
4865 element = EL_CHAR_Q;
4868 case 0x15f0: // (green)
4869 element = EL_CHAR_R;
4872 case 0x15f1: // (green)
4873 element = EL_CHAR_S;
4876 case 0x15f2: // (green)
4877 element = EL_CHAR_T;
4880 case 0x15f3: // (green)
4881 element = EL_CHAR_U;
4884 case 0x15f4: // (green)
4885 element = EL_CHAR_V;
4888 case 0x15f5: // (green)
4889 element = EL_CHAR_W;
4892 case 0x15f6: // (green)
4893 element = EL_CHAR_X;
4896 case 0x15f7: // (green)
4897 element = EL_CHAR_Y;
4900 case 0x15f8: // (green)
4901 element = EL_CHAR_Z;
4904 case 0x15f9: // (green)
4905 element = EL_CHAR_AUMLAUT;
4908 case 0x15fa: // (green)
4909 element = EL_CHAR_OUMLAUT;
4912 case 0x15fb: // (green)
4913 element = EL_CHAR_UUMLAUT;
4916 case 0x15fc: // (green)
4917 element = EL_CHAR_0;
4920 case 0x15fd: // (green)
4921 element = EL_CHAR_1;
4924 case 0x15fe: // (green)
4925 element = EL_CHAR_2;
4928 case 0x15ff: // (green)
4929 element = EL_CHAR_3;
4932 case 0x1600: // (green)
4933 element = EL_CHAR_4;
4936 case 0x1601: // (green)
4937 element = EL_CHAR_5;
4940 case 0x1602: // (green)
4941 element = EL_CHAR_6;
4944 case 0x1603: // (green)
4945 element = EL_CHAR_7;
4948 case 0x1604: // (green)
4949 element = EL_CHAR_8;
4952 case 0x1605: // (green)
4953 element = EL_CHAR_9;
4956 case 0x1606: // (green)
4957 element = EL_CHAR_PERIOD;
4960 case 0x1607: // (green)
4961 element = EL_CHAR_EXCLAM;
4964 case 0x1608: // (green)
4965 element = EL_CHAR_COLON;
4968 case 0x1609: // (green)
4969 element = EL_CHAR_LESS;
4972 case 0x160a: // (green)
4973 element = EL_CHAR_GREATER;
4976 case 0x160b: // (green)
4977 element = EL_CHAR_QUESTION;
4980 case 0x160c: // (green)
4981 element = EL_CHAR_COPYRIGHT;
4984 case 0x160d: // (green)
4985 element = EL_CHAR_UP;
4988 case 0x160e: // (green)
4989 element = EL_CHAR_DOWN;
4992 case 0x160f: // (green)
4993 element = EL_CHAR_BUTTON;
4996 case 0x1610: // (green)
4997 element = EL_CHAR_PLUS;
5000 case 0x1611: // (green)
5001 element = EL_CHAR_MINUS;
5004 case 0x1612: // (green)
5005 element = EL_CHAR_APOSTROPHE;
5008 case 0x1613: // (green)
5009 element = EL_CHAR_PARENLEFT;
5012 case 0x1614: // (green)
5013 element = EL_CHAR_PARENRIGHT;
5016 case 0x1615: // (blue steel)
5017 element = EL_STEEL_CHAR_A;
5020 case 0x1616: // (blue steel)
5021 element = EL_STEEL_CHAR_B;
5024 case 0x1617: // (blue steel)
5025 element = EL_STEEL_CHAR_C;
5028 case 0x1618: // (blue steel)
5029 element = EL_STEEL_CHAR_D;
5032 case 0x1619: // (blue steel)
5033 element = EL_STEEL_CHAR_E;
5036 case 0x161a: // (blue steel)
5037 element = EL_STEEL_CHAR_F;
5040 case 0x161b: // (blue steel)
5041 element = EL_STEEL_CHAR_G;
5044 case 0x161c: // (blue steel)
5045 element = EL_STEEL_CHAR_H;
5048 case 0x161d: // (blue steel)
5049 element = EL_STEEL_CHAR_I;
5052 case 0x161e: // (blue steel)
5053 element = EL_STEEL_CHAR_J;
5056 case 0x161f: // (blue steel)
5057 element = EL_STEEL_CHAR_K;
5060 case 0x1620: // (blue steel)
5061 element = EL_STEEL_CHAR_L;
5064 case 0x1621: // (blue steel)
5065 element = EL_STEEL_CHAR_M;
5068 case 0x1622: // (blue steel)
5069 element = EL_STEEL_CHAR_N;
5072 case 0x1623: // (blue steel)
5073 element = EL_STEEL_CHAR_O;
5076 case 0x1624: // (blue steel)
5077 element = EL_STEEL_CHAR_P;
5080 case 0x1625: // (blue steel)
5081 element = EL_STEEL_CHAR_Q;
5084 case 0x1626: // (blue steel)
5085 element = EL_STEEL_CHAR_R;
5088 case 0x1627: // (blue steel)
5089 element = EL_STEEL_CHAR_S;
5092 case 0x1628: // (blue steel)
5093 element = EL_STEEL_CHAR_T;
5096 case 0x1629: // (blue steel)
5097 element = EL_STEEL_CHAR_U;
5100 case 0x162a: // (blue steel)
5101 element = EL_STEEL_CHAR_V;
5104 case 0x162b: // (blue steel)
5105 element = EL_STEEL_CHAR_W;
5108 case 0x162c: // (blue steel)
5109 element = EL_STEEL_CHAR_X;
5112 case 0x162d: // (blue steel)
5113 element = EL_STEEL_CHAR_Y;
5116 case 0x162e: // (blue steel)
5117 element = EL_STEEL_CHAR_Z;
5120 case 0x162f: // (blue steel)
5121 element = EL_STEEL_CHAR_AUMLAUT;
5124 case 0x1630: // (blue steel)
5125 element = EL_STEEL_CHAR_OUMLAUT;
5128 case 0x1631: // (blue steel)
5129 element = EL_STEEL_CHAR_UUMLAUT;
5132 case 0x1632: // (blue steel)
5133 element = EL_STEEL_CHAR_0;
5136 case 0x1633: // (blue steel)
5137 element = EL_STEEL_CHAR_1;
5140 case 0x1634: // (blue steel)
5141 element = EL_STEEL_CHAR_2;
5144 case 0x1635: // (blue steel)
5145 element = EL_STEEL_CHAR_3;
5148 case 0x1636: // (blue steel)
5149 element = EL_STEEL_CHAR_4;
5152 case 0x1637: // (blue steel)
5153 element = EL_STEEL_CHAR_5;
5156 case 0x1638: // (blue steel)
5157 element = EL_STEEL_CHAR_6;
5160 case 0x1639: // (blue steel)
5161 element = EL_STEEL_CHAR_7;
5164 case 0x163a: // (blue steel)
5165 element = EL_STEEL_CHAR_8;
5168 case 0x163b: // (blue steel)
5169 element = EL_STEEL_CHAR_9;
5172 case 0x163c: // (blue steel)
5173 element = EL_STEEL_CHAR_PERIOD;
5176 case 0x163d: // (blue steel)
5177 element = EL_STEEL_CHAR_EXCLAM;
5180 case 0x163e: // (blue steel)
5181 element = EL_STEEL_CHAR_COLON;
5184 case 0x163f: // (blue steel)
5185 element = EL_STEEL_CHAR_LESS;
5188 case 0x1640: // (blue steel)
5189 element = EL_STEEL_CHAR_GREATER;
5192 case 0x1641: // (blue steel)
5193 element = EL_STEEL_CHAR_QUESTION;
5196 case 0x1642: // (blue steel)
5197 element = EL_STEEL_CHAR_COPYRIGHT;
5200 case 0x1643: // (blue steel)
5201 element = EL_STEEL_CHAR_UP;
5204 case 0x1644: // (blue steel)
5205 element = EL_STEEL_CHAR_DOWN;
5208 case 0x1645: // (blue steel)
5209 element = EL_STEEL_CHAR_BUTTON;
5212 case 0x1646: // (blue steel)
5213 element = EL_STEEL_CHAR_PLUS;
5216 case 0x1647: // (blue steel)
5217 element = EL_STEEL_CHAR_MINUS;
5220 case 0x1648: // (blue steel)
5221 element = EL_STEEL_CHAR_APOSTROPHE;
5224 case 0x1649: // (blue steel)
5225 element = EL_STEEL_CHAR_PARENLEFT;
5228 case 0x164a: // (blue steel)
5229 element = EL_STEEL_CHAR_PARENRIGHT;
5232 case 0x164b: // (green steel)
5233 element = EL_STEEL_CHAR_A;
5236 case 0x164c: // (green steel)
5237 element = EL_STEEL_CHAR_B;
5240 case 0x164d: // (green steel)
5241 element = EL_STEEL_CHAR_C;
5244 case 0x164e: // (green steel)
5245 element = EL_STEEL_CHAR_D;
5248 case 0x164f: // (green steel)
5249 element = EL_STEEL_CHAR_E;
5252 case 0x1650: // (green steel)
5253 element = EL_STEEL_CHAR_F;
5256 case 0x1651: // (green steel)
5257 element = EL_STEEL_CHAR_G;
5260 case 0x1652: // (green steel)
5261 element = EL_STEEL_CHAR_H;
5264 case 0x1653: // (green steel)
5265 element = EL_STEEL_CHAR_I;
5268 case 0x1654: // (green steel)
5269 element = EL_STEEL_CHAR_J;
5272 case 0x1655: // (green steel)
5273 element = EL_STEEL_CHAR_K;
5276 case 0x1656: // (green steel)
5277 element = EL_STEEL_CHAR_L;
5280 case 0x1657: // (green steel)
5281 element = EL_STEEL_CHAR_M;
5284 case 0x1658: // (green steel)
5285 element = EL_STEEL_CHAR_N;
5288 case 0x1659: // (green steel)
5289 element = EL_STEEL_CHAR_O;
5292 case 0x165a: // (green steel)
5293 element = EL_STEEL_CHAR_P;
5296 case 0x165b: // (green steel)
5297 element = EL_STEEL_CHAR_Q;
5300 case 0x165c: // (green steel)
5301 element = EL_STEEL_CHAR_R;
5304 case 0x165d: // (green steel)
5305 element = EL_STEEL_CHAR_S;
5308 case 0x165e: // (green steel)
5309 element = EL_STEEL_CHAR_T;
5312 case 0x165f: // (green steel)
5313 element = EL_STEEL_CHAR_U;
5316 case 0x1660: // (green steel)
5317 element = EL_STEEL_CHAR_V;
5320 case 0x1661: // (green steel)
5321 element = EL_STEEL_CHAR_W;
5324 case 0x1662: // (green steel)
5325 element = EL_STEEL_CHAR_X;
5328 case 0x1663: // (green steel)
5329 element = EL_STEEL_CHAR_Y;
5332 case 0x1664: // (green steel)
5333 element = EL_STEEL_CHAR_Z;
5336 case 0x1665: // (green steel)
5337 element = EL_STEEL_CHAR_AUMLAUT;
5340 case 0x1666: // (green steel)
5341 element = EL_STEEL_CHAR_OUMLAUT;
5344 case 0x1667: // (green steel)
5345 element = EL_STEEL_CHAR_UUMLAUT;
5348 case 0x1668: // (green steel)
5349 element = EL_STEEL_CHAR_0;
5352 case 0x1669: // (green steel)
5353 element = EL_STEEL_CHAR_1;
5356 case 0x166a: // (green steel)
5357 element = EL_STEEL_CHAR_2;
5360 case 0x166b: // (green steel)
5361 element = EL_STEEL_CHAR_3;
5364 case 0x166c: // (green steel)
5365 element = EL_STEEL_CHAR_4;
5368 case 0x166d: // (green steel)
5369 element = EL_STEEL_CHAR_5;
5372 case 0x166e: // (green steel)
5373 element = EL_STEEL_CHAR_6;
5376 case 0x166f: // (green steel)
5377 element = EL_STEEL_CHAR_7;
5380 case 0x1670: // (green steel)
5381 element = EL_STEEL_CHAR_8;
5384 case 0x1671: // (green steel)
5385 element = EL_STEEL_CHAR_9;
5388 case 0x1672: // (green steel)
5389 element = EL_STEEL_CHAR_PERIOD;
5392 case 0x1673: // (green steel)
5393 element = EL_STEEL_CHAR_EXCLAM;
5396 case 0x1674: // (green steel)
5397 element = EL_STEEL_CHAR_COLON;
5400 case 0x1675: // (green steel)
5401 element = EL_STEEL_CHAR_LESS;
5404 case 0x1676: // (green steel)
5405 element = EL_STEEL_CHAR_GREATER;
5408 case 0x1677: // (green steel)
5409 element = EL_STEEL_CHAR_QUESTION;
5412 case 0x1678: // (green steel)
5413 element = EL_STEEL_CHAR_COPYRIGHT;
5416 case 0x1679: // (green steel)
5417 element = EL_STEEL_CHAR_UP;
5420 case 0x167a: // (green steel)
5421 element = EL_STEEL_CHAR_DOWN;
5424 case 0x167b: // (green steel)
5425 element = EL_STEEL_CHAR_BUTTON;
5428 case 0x167c: // (green steel)
5429 element = EL_STEEL_CHAR_PLUS;
5432 case 0x167d: // (green steel)
5433 element = EL_STEEL_CHAR_MINUS;
5436 case 0x167e: // (green steel)
5437 element = EL_STEEL_CHAR_APOSTROPHE;
5440 case 0x167f: // (green steel)
5441 element = EL_STEEL_CHAR_PARENLEFT;
5444 case 0x1680: // (green steel)
5445 element = EL_STEEL_CHAR_PARENRIGHT;
5448 case 0x1681: // gate (red)
5449 element = EL_EM_GATE_1;
5452 case 0x1682: // secret gate (red)
5453 element = EL_EM_GATE_1_GRAY;
5456 case 0x1683: // gate (yellow)
5457 element = EL_EM_GATE_2;
5460 case 0x1684: // secret gate (yellow)
5461 element = EL_EM_GATE_2_GRAY;
5464 case 0x1685: // gate (blue)
5465 element = EL_EM_GATE_4;
5468 case 0x1686: // secret gate (blue)
5469 element = EL_EM_GATE_4_GRAY;
5472 case 0x1687: // gate (green)
5473 element = EL_EM_GATE_3;
5476 case 0x1688: // secret gate (green)
5477 element = EL_EM_GATE_3_GRAY;
5480 case 0x1689: // gate (white)
5481 element = EL_DC_GATE_WHITE;
5484 case 0x168a: // secret gate (white)
5485 element = EL_DC_GATE_WHITE_GRAY;
5488 case 0x168b: // secret gate (no key)
5489 element = EL_DC_GATE_FAKE_GRAY;
5493 element = EL_ROBOT_WHEEL;
5497 element = EL_DC_TIMEGATE_SWITCH;
5501 element = EL_ACID_POOL_BOTTOM;
5505 element = EL_ACID_POOL_TOPLEFT;
5509 element = EL_ACID_POOL_TOPRIGHT;
5513 element = EL_ACID_POOL_BOTTOMLEFT;
5517 element = EL_ACID_POOL_BOTTOMRIGHT;
5521 element = EL_STEELWALL;
5525 element = EL_STEELWALL_SLIPPERY;
5528 case 0x1695: // steel wall (not round)
5529 element = EL_STEELWALL;
5532 case 0x1696: // steel wall (left)
5533 element = EL_DC_STEELWALL_1_LEFT;
5536 case 0x1697: // steel wall (bottom)
5537 element = EL_DC_STEELWALL_1_BOTTOM;
5540 case 0x1698: // steel wall (right)
5541 element = EL_DC_STEELWALL_1_RIGHT;
5544 case 0x1699: // steel wall (top)
5545 element = EL_DC_STEELWALL_1_TOP;
5548 case 0x169a: // steel wall (left/bottom)
5549 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5552 case 0x169b: // steel wall (right/bottom)
5553 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5556 case 0x169c: // steel wall (right/top)
5557 element = EL_DC_STEELWALL_1_TOPRIGHT;
5560 case 0x169d: // steel wall (left/top)
5561 element = EL_DC_STEELWALL_1_TOPLEFT;
5564 case 0x169e: // steel wall (right/bottom small)
5565 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5568 case 0x169f: // steel wall (left/bottom small)
5569 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5572 case 0x16a0: // steel wall (right/top small)
5573 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5576 case 0x16a1: // steel wall (left/top small)
5577 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5580 case 0x16a2: // steel wall (left/right)
5581 element = EL_DC_STEELWALL_1_VERTICAL;
5584 case 0x16a3: // steel wall (top/bottom)
5585 element = EL_DC_STEELWALL_1_HORIZONTAL;
5588 case 0x16a4: // steel wall 2 (left end)
5589 element = EL_DC_STEELWALL_2_LEFT;
5592 case 0x16a5: // steel wall 2 (right end)
5593 element = EL_DC_STEELWALL_2_RIGHT;
5596 case 0x16a6: // steel wall 2 (top end)
5597 element = EL_DC_STEELWALL_2_TOP;
5600 case 0x16a7: // steel wall 2 (bottom end)
5601 element = EL_DC_STEELWALL_2_BOTTOM;
5604 case 0x16a8: // steel wall 2 (left/right)
5605 element = EL_DC_STEELWALL_2_HORIZONTAL;
5608 case 0x16a9: // steel wall 2 (up/down)
5609 element = EL_DC_STEELWALL_2_VERTICAL;
5612 case 0x16aa: // steel wall 2 (mid)
5613 element = EL_DC_STEELWALL_2_MIDDLE;
5617 element = EL_SIGN_EXCLAMATION;
5621 element = EL_SIGN_RADIOACTIVITY;
5625 element = EL_SIGN_STOP;
5629 element = EL_SIGN_WHEELCHAIR;
5633 element = EL_SIGN_PARKING;
5637 element = EL_SIGN_NO_ENTRY;
5641 element = EL_SIGN_HEART;
5645 element = EL_SIGN_GIVE_WAY;
5649 element = EL_SIGN_ENTRY_FORBIDDEN;
5653 element = EL_SIGN_EMERGENCY_EXIT;
5657 element = EL_SIGN_YIN_YANG;
5661 element = EL_WALL_EMERALD;
5665 element = EL_WALL_DIAMOND;
5669 element = EL_WALL_PEARL;
5673 element = EL_WALL_CRYSTAL;
5677 element = EL_INVISIBLE_WALL;
5681 element = EL_INVISIBLE_STEELWALL;
5685 // EL_INVISIBLE_SAND
5688 element = EL_LIGHT_SWITCH;
5692 element = EL_ENVELOPE_1;
5696 if (element >= 0x0117 && element <= 0x036e) // (?)
5697 element = EL_DIAMOND;
5698 else if (element >= 0x042d && element <= 0x0684) // (?)
5699 element = EL_EMERALD;
5700 else if (element >= 0x157c && element <= 0x158b)
5702 else if (element >= 0x1590 && element <= 0x159f)
5703 element = EL_DC_LANDMINE;
5704 else if (element >= 0x16bc && element <= 0x16cb)
5705 element = EL_INVISIBLE_SAND;
5708 Warn("unknown Diamond Caves element 0x%04x", element);
5710 element = EL_UNKNOWN;
5715 return getMappedElement(element);
5718 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5720 byte header[DC_LEVEL_HEADER_SIZE];
5722 int envelope_header_pos = 62;
5723 int envelope_content_pos = 94;
5724 int level_name_pos = 251;
5725 int level_author_pos = 292;
5726 int envelope_header_len;
5727 int envelope_content_len;
5729 int level_author_len;
5731 int num_yamyam_contents;
5734 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5736 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5738 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5740 header[i * 2 + 0] = header_word >> 8;
5741 header[i * 2 + 1] = header_word & 0xff;
5744 // read some values from level header to check level decoding integrity
5745 fieldx = header[6] | (header[7] << 8);
5746 fieldy = header[8] | (header[9] << 8);
5747 num_yamyam_contents = header[60] | (header[61] << 8);
5749 // do some simple sanity checks to ensure that level was correctly decoded
5750 if (fieldx < 1 || fieldx > 256 ||
5751 fieldy < 1 || fieldy > 256 ||
5752 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5754 level->no_valid_file = TRUE;
5756 Warn("cannot decode level from stream -- using empty level");
5761 // maximum envelope header size is 31 bytes
5762 envelope_header_len = header[envelope_header_pos];
5763 // maximum envelope content size is 110 (156?) bytes
5764 envelope_content_len = header[envelope_content_pos];
5766 // maximum level title size is 40 bytes
5767 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5768 // maximum level author size is 30 (51?) bytes
5769 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5773 for (i = 0; i < envelope_header_len; i++)
5774 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5775 level->envelope[0].text[envelope_size++] =
5776 header[envelope_header_pos + 1 + i];
5778 if (envelope_header_len > 0 && envelope_content_len > 0)
5780 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5781 level->envelope[0].text[envelope_size++] = '\n';
5782 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5783 level->envelope[0].text[envelope_size++] = '\n';
5786 for (i = 0; i < envelope_content_len; i++)
5787 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5788 level->envelope[0].text[envelope_size++] =
5789 header[envelope_content_pos + 1 + i];
5791 level->envelope[0].text[envelope_size] = '\0';
5793 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5794 level->envelope[0].ysize = 10;
5795 level->envelope[0].autowrap = TRUE;
5796 level->envelope[0].centered = TRUE;
5798 for (i = 0; i < level_name_len; i++)
5799 level->name[i] = header[level_name_pos + 1 + i];
5800 level->name[level_name_len] = '\0';
5802 for (i = 0; i < level_author_len; i++)
5803 level->author[i] = header[level_author_pos + 1 + i];
5804 level->author[level_author_len] = '\0';
5806 num_yamyam_contents = header[60] | (header[61] << 8);
5807 level->num_yamyam_contents =
5808 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5810 for (i = 0; i < num_yamyam_contents; i++)
5812 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5814 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5815 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5817 if (i < MAX_ELEMENT_CONTENTS)
5818 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5822 fieldx = header[6] | (header[7] << 8);
5823 fieldy = header[8] | (header[9] << 8);
5824 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5825 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5827 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5829 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5830 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5832 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5833 level->field[x][y] = getMappedElement_DC(element_dc);
5836 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5837 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5838 level->field[x][y] = EL_PLAYER_1;
5840 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5841 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5842 level->field[x][y] = EL_PLAYER_2;
5844 level->gems_needed = header[18] | (header[19] << 8);
5846 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5847 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5848 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5849 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5850 level->score[SC_NUT] = header[28] | (header[29] << 8);
5851 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5852 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5853 level->score[SC_BUG] = header[34] | (header[35] << 8);
5854 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5855 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5856 level->score[SC_KEY] = header[40] | (header[41] << 8);
5857 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5859 level->time = header[44] | (header[45] << 8);
5861 level->amoeba_speed = header[46] | (header[47] << 8);
5862 level->time_light = header[48] | (header[49] << 8);
5863 level->time_timegate = header[50] | (header[51] << 8);
5864 level->time_wheel = header[52] | (header[53] << 8);
5865 level->time_magic_wall = header[54] | (header[55] << 8);
5866 level->extra_time = header[56] | (header[57] << 8);
5867 level->shield_normal_time = header[58] | (header[59] << 8);
5869 // shield and extra time elements do not have a score
5870 level->score[SC_SHIELD] = 0;
5871 level->extra_time_score = 0;
5873 // set time for normal and deadly shields to the same value
5874 level->shield_deadly_time = level->shield_normal_time;
5876 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5877 // can slip down from flat walls, like normal walls and steel walls
5878 level->em_slippery_gems = TRUE;
5880 // time score is counted for each 10 seconds left in Diamond Caves levels
5881 level->time_score_base = 10;
5884 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5885 struct LevelFileInfo *level_file_info,
5886 boolean level_info_only)
5888 char *filename = level_file_info->filename;
5890 int num_magic_bytes = 8;
5891 char magic_bytes[num_magic_bytes + 1];
5892 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5894 if (!(file = openFile(filename, MODE_READ)))
5896 level->no_valid_file = TRUE;
5898 if (!level_info_only)
5899 Warn("cannot read level '%s' -- using empty level", filename);
5904 // fseek(file, 0x0000, SEEK_SET);
5906 if (level_file_info->packed)
5908 // read "magic bytes" from start of file
5909 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5910 magic_bytes[0] = '\0';
5912 // check "magic bytes" for correct file format
5913 if (!strPrefix(magic_bytes, "DC2"))
5915 level->no_valid_file = TRUE;
5917 Warn("unknown DC level file '%s' -- using empty level", filename);
5922 if (strPrefix(magic_bytes, "DC2Win95") ||
5923 strPrefix(magic_bytes, "DC2Win98"))
5925 int position_first_level = 0x00fa;
5926 int extra_bytes = 4;
5929 // advance file stream to first level inside the level package
5930 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5932 // each block of level data is followed by block of non-level data
5933 num_levels_to_skip *= 2;
5935 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5936 while (num_levels_to_skip >= 0)
5938 // advance file stream to next level inside the level package
5939 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5941 level->no_valid_file = TRUE;
5943 Warn("cannot fseek in file '%s' -- using empty level", filename);
5948 // skip apparently unused extra bytes following each level
5949 ReadUnusedBytesFromFile(file, extra_bytes);
5951 // read size of next level in level package
5952 skip_bytes = getFile32BitLE(file);
5954 num_levels_to_skip--;
5959 level->no_valid_file = TRUE;
5961 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5967 LoadLevelFromFileStream_DC(file, level);
5973 // ----------------------------------------------------------------------------
5974 // functions for loading SB level
5975 // ----------------------------------------------------------------------------
5977 int getMappedElement_SB(int element_ascii, boolean use_ces)
5985 sb_element_mapping[] =
5987 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5988 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5989 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5990 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5991 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5992 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5993 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5994 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6001 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6002 if (element_ascii == sb_element_mapping[i].ascii)
6003 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6005 return EL_UNDEFINED;
6008 static void SetLevelSettings_SB(struct LevelInfo *level)
6012 level->use_step_counter = TRUE;
6015 level->score[SC_TIME_BONUS] = 0;
6016 level->time_score_base = 1;
6017 level->rate_time_over_score = TRUE;
6020 level->auto_exit_sokoban = TRUE;
6023 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6024 struct LevelFileInfo *level_file_info,
6025 boolean level_info_only)
6027 char *filename = level_file_info->filename;
6028 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6029 char last_comment[MAX_LINE_LEN];
6030 char level_name[MAX_LINE_LEN];
6033 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6034 boolean read_continued_line = FALSE;
6035 boolean reading_playfield = FALSE;
6036 boolean got_valid_playfield_line = FALSE;
6037 boolean invalid_playfield_char = FALSE;
6038 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6039 int file_level_nr = 0;
6041 int x = 0, y = 0; // initialized to make compilers happy
6043 last_comment[0] = '\0';
6044 level_name[0] = '\0';
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 while (!checkEndOfFile(file))
6058 // level successfully read, but next level may follow here
6059 if (!got_valid_playfield_line && reading_playfield)
6061 // read playfield from single level file -- skip remaining file
6062 if (!level_file_info->packed)
6065 if (file_level_nr >= num_levels_to_skip)
6070 last_comment[0] = '\0';
6071 level_name[0] = '\0';
6073 reading_playfield = FALSE;
6076 got_valid_playfield_line = FALSE;
6078 // read next line of input file
6079 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6082 // check if line was completely read and is terminated by line break
6083 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6086 // cut trailing line break (this can be newline and/or carriage return)
6087 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6088 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6091 // copy raw input line for later use (mainly debugging output)
6092 strcpy(line_raw, line);
6094 if (read_continued_line)
6096 // append new line to existing line, if there is enough space
6097 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6098 strcat(previous_line, line_ptr);
6100 strcpy(line, previous_line); // copy storage buffer to line
6102 read_continued_line = FALSE;
6105 // if the last character is '\', continue at next line
6106 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6108 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6109 strcpy(previous_line, line); // copy line to storage buffer
6111 read_continued_line = TRUE;
6117 if (line[0] == '\0')
6120 // extract comment text from comment line
6123 for (line_ptr = line; *line_ptr; line_ptr++)
6124 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6127 strcpy(last_comment, line_ptr);
6132 // extract level title text from line containing level title
6133 if (line[0] == '\'')
6135 strcpy(level_name, &line[1]);
6137 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6138 level_name[strlen(level_name) - 1] = '\0';
6143 // skip lines containing only spaces (or empty lines)
6144 for (line_ptr = line; *line_ptr; line_ptr++)
6145 if (*line_ptr != ' ')
6147 if (*line_ptr == '\0')
6150 // at this point, we have found a line containing part of a playfield
6152 got_valid_playfield_line = TRUE;
6154 if (!reading_playfield)
6156 reading_playfield = TRUE;
6157 invalid_playfield_char = FALSE;
6159 for (x = 0; x < MAX_LEV_FIELDX; x++)
6160 for (y = 0; y < MAX_LEV_FIELDY; y++)
6161 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6166 // start with topmost tile row
6170 // skip playfield line if larger row than allowed
6171 if (y >= MAX_LEV_FIELDY)
6174 // start with leftmost tile column
6177 // read playfield elements from line
6178 for (line_ptr = line; *line_ptr; line_ptr++)
6180 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6182 // stop parsing playfield line if larger column than allowed
6183 if (x >= MAX_LEV_FIELDX)
6186 if (mapped_sb_element == EL_UNDEFINED)
6188 invalid_playfield_char = TRUE;
6193 level->field[x][y] = mapped_sb_element;
6195 // continue with next tile column
6198 level->fieldx = MAX(x, level->fieldx);
6201 if (invalid_playfield_char)
6203 // if first playfield line, treat invalid lines as comment lines
6205 reading_playfield = FALSE;
6210 // continue with next tile row
6218 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6219 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6221 if (!reading_playfield)
6223 level->no_valid_file = TRUE;
6225 Warn("cannot read level '%s' -- using empty level", filename);
6230 if (*level_name != '\0')
6232 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6233 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6235 else if (*last_comment != '\0')
6237 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6238 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6242 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6245 // set all empty fields beyond the border walls to invisible steel wall
6246 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6248 if ((x == 0 || x == level->fieldx - 1 ||
6249 y == 0 || y == level->fieldy - 1) &&
6250 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6251 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6252 level->field, level->fieldx, level->fieldy);
6255 // set special level settings for Sokoban levels
6256 SetLevelSettings_SB(level);
6258 if (load_xsb_to_ces)
6260 // special global settings can now be set in level template
6261 level->use_custom_template = TRUE;
6266 // -------------------------------------------------------------------------
6267 // functions for handling native levels
6268 // -------------------------------------------------------------------------
6270 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6271 struct LevelFileInfo *level_file_info,
6272 boolean level_info_only)
6274 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6275 level->no_valid_file = TRUE;
6278 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6279 struct LevelFileInfo *level_file_info,
6280 boolean level_info_only)
6284 // determine position of requested level inside level package
6285 if (level_file_info->packed)
6286 pos = level_file_info->nr - leveldir_current->first_level;
6288 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6289 level->no_valid_file = TRUE;
6292 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6293 struct LevelFileInfo *level_file_info,
6294 boolean level_info_only)
6296 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6297 level->no_valid_file = TRUE;
6300 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6302 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6303 CopyNativeLevel_RND_to_EM(level);
6304 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6305 CopyNativeLevel_RND_to_SP(level);
6306 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6307 CopyNativeLevel_RND_to_MM(level);
6310 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6312 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6313 CopyNativeLevel_EM_to_RND(level);
6314 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6315 CopyNativeLevel_SP_to_RND(level);
6316 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6317 CopyNativeLevel_MM_to_RND(level);
6320 void SaveNativeLevel(struct LevelInfo *level)
6322 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6324 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6325 char *filename = getLevelFilenameFromBasename(basename);
6327 CopyNativeLevel_RND_to_SP(level);
6328 CopyNativeTape_RND_to_SP(level);
6330 SaveNativeLevel_SP(filename);
6335 // ----------------------------------------------------------------------------
6336 // functions for loading generic level
6337 // ----------------------------------------------------------------------------
6339 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6340 struct LevelFileInfo *level_file_info,
6341 boolean level_info_only)
6343 // always start with reliable default values
6344 setLevelInfoToDefaults(level, level_info_only, TRUE);
6346 switch (level_file_info->type)
6348 case LEVEL_FILE_TYPE_RND:
6349 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6352 case LEVEL_FILE_TYPE_EM:
6353 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6354 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6357 case LEVEL_FILE_TYPE_SP:
6358 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6359 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6362 case LEVEL_FILE_TYPE_MM:
6363 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6364 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6367 case LEVEL_FILE_TYPE_DC:
6368 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6371 case LEVEL_FILE_TYPE_SB:
6372 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6376 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6380 // if level file is invalid, restore level structure to default values
6381 if (level->no_valid_file)
6382 setLevelInfoToDefaults(level, level_info_only, FALSE);
6384 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6385 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6387 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6388 CopyNativeLevel_Native_to_RND(level);
6391 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6393 static struct LevelFileInfo level_file_info;
6395 // always start with reliable default values
6396 setFileInfoToDefaults(&level_file_info);
6398 level_file_info.nr = 0; // unknown level number
6399 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6401 setString(&level_file_info.filename, filename);
6403 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6406 static void LoadLevel_InitVersion(struct LevelInfo *level)
6410 if (leveldir_current == NULL) // only when dumping level
6413 // all engine modifications also valid for levels which use latest engine
6414 if (level->game_version < VERSION_IDENT(3,2,0,5))
6416 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6417 level->time_score_base = 10;
6420 if (leveldir_current->latest_engine)
6422 // ---------- use latest game engine --------------------------------------
6424 /* For all levels which are forced to use the latest game engine version
6425 (normally all but user contributed, private and undefined levels), set
6426 the game engine version to the actual version; this allows for actual
6427 corrections in the game engine to take effect for existing, converted
6428 levels (from "classic" or other existing games) to make the emulation
6429 of the corresponding game more accurate, while (hopefully) not breaking
6430 existing levels created from other players. */
6432 level->game_version = GAME_VERSION_ACTUAL;
6434 /* Set special EM style gems behaviour: EM style gems slip down from
6435 normal, steel and growing wall. As this is a more fundamental change,
6436 it seems better to set the default behaviour to "off" (as it is more
6437 natural) and make it configurable in the level editor (as a property
6438 of gem style elements). Already existing converted levels (neither
6439 private nor contributed levels) are changed to the new behaviour. */
6441 if (level->file_version < FILE_VERSION_2_0)
6442 level->em_slippery_gems = TRUE;
6447 // ---------- use game engine the level was created with --------------------
6449 /* For all levels which are not forced to use the latest game engine
6450 version (normally user contributed, private and undefined levels),
6451 use the version of the game engine the levels were created for.
6453 Since 2.0.1, the game engine version is now directly stored
6454 in the level file (chunk "VERS"), so there is no need anymore
6455 to set the game version from the file version (except for old,
6456 pre-2.0 levels, where the game version is still taken from the
6457 file format version used to store the level -- see above). */
6459 // player was faster than enemies in 1.0.0 and before
6460 if (level->file_version == FILE_VERSION_1_0)
6461 for (i = 0; i < MAX_PLAYERS; i++)
6462 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6464 // default behaviour for EM style gems was "slippery" only in 2.0.1
6465 if (level->game_version == VERSION_IDENT(2,0,1,0))
6466 level->em_slippery_gems = TRUE;
6468 // springs could be pushed over pits before (pre-release version) 2.2.0
6469 if (level->game_version < VERSION_IDENT(2,2,0,0))
6470 level->use_spring_bug = TRUE;
6472 if (level->game_version < VERSION_IDENT(3,2,0,5))
6474 // time orb caused limited time in endless time levels before 3.2.0-5
6475 level->use_time_orb_bug = TRUE;
6477 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6478 level->block_snap_field = FALSE;
6480 // extra time score was same value as time left score before 3.2.0-5
6481 level->extra_time_score = level->score[SC_TIME_BONUS];
6484 if (level->game_version < VERSION_IDENT(3,2,0,7))
6486 // default behaviour for snapping was "not continuous" before 3.2.0-7
6487 level->continuous_snapping = FALSE;
6490 // only few elements were able to actively move into acid before 3.1.0
6491 // trigger settings did not exist before 3.1.0; set to default "any"
6492 if (level->game_version < VERSION_IDENT(3,1,0,0))
6494 // correct "can move into acid" settings (all zero in old levels)
6496 level->can_move_into_acid_bits = 0; // nothing can move into acid
6497 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6499 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6500 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6501 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6502 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6504 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6505 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6507 // correct trigger settings (stored as zero == "none" in old levels)
6509 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6511 int element = EL_CUSTOM_START + i;
6512 struct ElementInfo *ei = &element_info[element];
6514 for (j = 0; j < ei->num_change_pages; j++)
6516 struct ElementChangeInfo *change = &ei->change_page[j];
6518 change->trigger_player = CH_PLAYER_ANY;
6519 change->trigger_page = CH_PAGE_ANY;
6524 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6526 int element = EL_CUSTOM_256;
6527 struct ElementInfo *ei = &element_info[element];
6528 struct ElementChangeInfo *change = &ei->change_page[0];
6530 /* This is needed to fix a problem that was caused by a bugfix in function
6531 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6532 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6533 not replace walkable elements, but instead just placed the player on it,
6534 without placing the Sokoban field under the player). Unfortunately, this
6535 breaks "Snake Bite" style levels when the snake is halfway through a door
6536 that just closes (the snake head is still alive and can be moved in this
6537 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6538 player (without Sokoban element) which then gets killed as designed). */
6540 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6541 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6542 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6543 change->target_element = EL_PLAYER_1;
6546 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6547 if (level->game_version < VERSION_IDENT(3,2,5,0))
6549 /* This is needed to fix a problem that was caused by a bugfix in function
6550 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6551 corrects the behaviour when a custom element changes to another custom
6552 element with a higher element number that has change actions defined.
6553 Normally, only one change per frame is allowed for custom elements.
6554 Therefore, it is checked if a custom element already changed in the
6555 current frame; if it did, subsequent changes are suppressed.
6556 Unfortunately, this is only checked for element changes, but not for
6557 change actions, which are still executed. As the function above loops
6558 through all custom elements from lower to higher, an element change
6559 resulting in a lower CE number won't be checked again, while a target
6560 element with a higher number will also be checked, and potential change
6561 actions will get executed for this CE, too (which is wrong), while
6562 further changes are ignored (which is correct). As this bugfix breaks
6563 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6564 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6565 behaviour for existing levels and tapes that make use of this bug */
6567 level->use_action_after_change_bug = TRUE;
6570 // not centering level after relocating player was default only in 3.2.3
6571 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6572 level->shifted_relocation = TRUE;
6574 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6575 if (level->game_version < VERSION_IDENT(3,2,6,0))
6576 level->em_explodes_by_fire = TRUE;
6578 // levels were solved by the first player entering an exit up to 4.1.0.0
6579 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6580 level->solved_by_one_player = TRUE;
6582 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6583 if (level->game_version < VERSION_IDENT(4,1,1,1))
6584 level->use_life_bugs = TRUE;
6586 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6587 if (level->game_version < VERSION_IDENT(4,1,1,1))
6588 level->sb_objects_needed = FALSE;
6590 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6591 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6592 level->finish_dig_collect = FALSE;
6594 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6595 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6596 level->keep_walkable_ce = TRUE;
6599 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6601 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6604 // check if this level is (not) a Sokoban level
6605 for (y = 0; y < level->fieldy; y++)
6606 for (x = 0; x < level->fieldx; x++)
6607 if (!IS_SB_ELEMENT(Tile[x][y]))
6608 is_sokoban_level = FALSE;
6610 if (is_sokoban_level)
6612 // set special level settings for Sokoban levels
6613 SetLevelSettings_SB(level);
6617 static void LoadLevel_InitSettings(struct LevelInfo *level)
6619 // adjust level settings for (non-native) Sokoban-style levels
6620 LoadLevel_InitSettings_SB(level);
6622 // rename levels with title "nameless level" or if renaming is forced
6623 if (leveldir_current->empty_level_name != NULL &&
6624 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6625 leveldir_current->force_level_name))
6626 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6627 leveldir_current->empty_level_name, level_nr);
6630 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6634 // map elements that have changed in newer versions
6635 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6636 level->game_version);
6637 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6638 for (x = 0; x < 3; x++)
6639 for (y = 0; y < 3; y++)
6640 level->yamyam_content[i].e[x][y] =
6641 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6642 level->game_version);
6646 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6650 // map custom element change events that have changed in newer versions
6651 // (these following values were accidentally changed in version 3.0.1)
6652 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6653 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6655 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6657 int element = EL_CUSTOM_START + i;
6659 // order of checking and copying events to be mapped is important
6660 // (do not change the start and end value -- they are constant)
6661 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6663 if (HAS_CHANGE_EVENT(element, j - 2))
6665 SET_CHANGE_EVENT(element, j - 2, FALSE);
6666 SET_CHANGE_EVENT(element, j, TRUE);
6670 // order of checking and copying events to be mapped is important
6671 // (do not change the start and end value -- they are constant)
6672 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6674 if (HAS_CHANGE_EVENT(element, j - 1))
6676 SET_CHANGE_EVENT(element, j - 1, FALSE);
6677 SET_CHANGE_EVENT(element, j, TRUE);
6683 // initialize "can_change" field for old levels with only one change page
6684 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6686 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6688 int element = EL_CUSTOM_START + i;
6690 if (CAN_CHANGE(element))
6691 element_info[element].change->can_change = TRUE;
6695 // correct custom element values (for old levels without these options)
6696 if (level->game_version < VERSION_IDENT(3,1,1,0))
6698 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6700 int element = EL_CUSTOM_START + i;
6701 struct ElementInfo *ei = &element_info[element];
6703 if (ei->access_direction == MV_NO_DIRECTION)
6704 ei->access_direction = MV_ALL_DIRECTIONS;
6708 // correct custom element values (fix invalid values for all versions)
6711 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6713 int element = EL_CUSTOM_START + i;
6714 struct ElementInfo *ei = &element_info[element];
6716 for (j = 0; j < ei->num_change_pages; j++)
6718 struct ElementChangeInfo *change = &ei->change_page[j];
6720 if (change->trigger_player == CH_PLAYER_NONE)
6721 change->trigger_player = CH_PLAYER_ANY;
6723 if (change->trigger_side == CH_SIDE_NONE)
6724 change->trigger_side = CH_SIDE_ANY;
6729 // initialize "can_explode" field for old levels which did not store this
6730 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6731 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6733 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6735 int element = EL_CUSTOM_START + i;
6737 if (EXPLODES_1X1_OLD(element))
6738 element_info[element].explosion_type = EXPLODES_1X1;
6740 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6741 EXPLODES_SMASHED(element) ||
6742 EXPLODES_IMPACT(element)));
6746 // correct previously hard-coded move delay values for maze runner style
6747 if (level->game_version < VERSION_IDENT(3,1,1,0))
6749 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6751 int element = EL_CUSTOM_START + i;
6753 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6755 // previously hard-coded and therefore ignored
6756 element_info[element].move_delay_fixed = 9;
6757 element_info[element].move_delay_random = 0;
6762 // set some other uninitialized values of custom elements in older levels
6763 if (level->game_version < VERSION_IDENT(3,1,0,0))
6765 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6767 int element = EL_CUSTOM_START + i;
6769 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6771 element_info[element].explosion_delay = 17;
6772 element_info[element].ignition_delay = 8;
6776 // set mouse click change events to work for left/middle/right mouse button
6777 if (level->game_version < VERSION_IDENT(4,2,3,0))
6779 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6781 int element = EL_CUSTOM_START + i;
6782 struct ElementInfo *ei = &element_info[element];
6784 for (j = 0; j < ei->num_change_pages; j++)
6786 struct ElementChangeInfo *change = &ei->change_page[j];
6788 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6789 change->has_event[CE_PRESSED_BY_MOUSE] ||
6790 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6791 change->has_event[CE_MOUSE_PRESSED_ON_X])
6792 change->trigger_side = CH_SIDE_ANY;
6798 static void LoadLevel_InitElements(struct LevelInfo *level)
6800 LoadLevel_InitStandardElements(level);
6802 if (level->file_has_custom_elements)
6803 LoadLevel_InitCustomElements(level);
6805 // initialize element properties for level editor etc.
6806 InitElementPropertiesEngine(level->game_version);
6807 InitElementPropertiesGfxElement();
6810 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6814 // map elements that have changed in newer versions
6815 for (y = 0; y < level->fieldy; y++)
6816 for (x = 0; x < level->fieldx; x++)
6817 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6818 level->game_version);
6820 // clear unused playfield data (nicer if level gets resized in editor)
6821 for (x = 0; x < MAX_LEV_FIELDX; x++)
6822 for (y = 0; y < MAX_LEV_FIELDY; y++)
6823 if (x >= level->fieldx || y >= level->fieldy)
6824 level->field[x][y] = EL_EMPTY;
6826 // copy elements to runtime playfield array
6827 for (x = 0; x < MAX_LEV_FIELDX; x++)
6828 for (y = 0; y < MAX_LEV_FIELDY; y++)
6829 Tile[x][y] = level->field[x][y];
6831 // initialize level size variables for faster access
6832 lev_fieldx = level->fieldx;
6833 lev_fieldy = level->fieldy;
6835 // determine border element for this level
6836 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6837 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6842 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6844 struct LevelFileInfo *level_file_info = &level->file_info;
6846 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6847 CopyNativeLevel_RND_to_Native(level);
6850 static void LoadLevelTemplate_LoadAndInit(void)
6852 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6854 LoadLevel_InitVersion(&level_template);
6855 LoadLevel_InitElements(&level_template);
6856 LoadLevel_InitSettings(&level_template);
6858 ActivateLevelTemplate();
6861 void LoadLevelTemplate(int nr)
6863 if (!fileExists(getGlobalLevelTemplateFilename()))
6865 Warn("no level template found for this level");
6870 setLevelFileInfo(&level_template.file_info, nr);
6872 LoadLevelTemplate_LoadAndInit();
6875 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6877 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6879 LoadLevelTemplate_LoadAndInit();
6882 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6884 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6886 if (level.use_custom_template)
6888 if (network_level != NULL)
6889 LoadNetworkLevelTemplate(network_level);
6891 LoadLevelTemplate(-1);
6894 LoadLevel_InitVersion(&level);
6895 LoadLevel_InitElements(&level);
6896 LoadLevel_InitPlayfield(&level);
6897 LoadLevel_InitSettings(&level);
6899 LoadLevel_InitNativeEngines(&level);
6902 void LoadLevel(int nr)
6904 SetLevelSetInfo(leveldir_current->identifier, nr);
6906 setLevelFileInfo(&level.file_info, nr);
6908 LoadLevel_LoadAndInit(NULL);
6911 void LoadLevelInfoOnly(int nr)
6913 setLevelFileInfo(&level.file_info, nr);
6915 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6918 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6920 SetLevelSetInfo(network_level->leveldir_identifier,
6921 network_level->file_info.nr);
6923 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6925 LoadLevel_LoadAndInit(network_level);
6928 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6932 chunk_size += putFileVersion(file, level->file_version);
6933 chunk_size += putFileVersion(file, level->game_version);
6938 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6942 chunk_size += putFile16BitBE(file, level->creation_date.year);
6943 chunk_size += putFile8Bit(file, level->creation_date.month);
6944 chunk_size += putFile8Bit(file, level->creation_date.day);
6949 #if ENABLE_HISTORIC_CHUNKS
6950 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6954 putFile8Bit(file, level->fieldx);
6955 putFile8Bit(file, level->fieldy);
6957 putFile16BitBE(file, level->time);
6958 putFile16BitBE(file, level->gems_needed);
6960 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6961 putFile8Bit(file, level->name[i]);
6963 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6964 putFile8Bit(file, level->score[i]);
6966 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6967 for (y = 0; y < 3; y++)
6968 for (x = 0; x < 3; x++)
6969 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6970 level->yamyam_content[i].e[x][y]));
6971 putFile8Bit(file, level->amoeba_speed);
6972 putFile8Bit(file, level->time_magic_wall);
6973 putFile8Bit(file, level->time_wheel);
6974 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6975 level->amoeba_content));
6976 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6977 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6978 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6979 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6981 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6983 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6984 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6985 putFile32BitBE(file, level->can_move_into_acid_bits);
6986 putFile8Bit(file, level->dont_collide_with_bits);
6988 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6989 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6991 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6992 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6993 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6995 putFile8Bit(file, level->game_engine_type);
6997 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7001 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7006 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7007 chunk_size += putFile8Bit(file, level->name[i]);
7012 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7017 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7018 chunk_size += putFile8Bit(file, level->author[i]);
7023 #if ENABLE_HISTORIC_CHUNKS
7024 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7029 for (y = 0; y < level->fieldy; y++)
7030 for (x = 0; x < level->fieldx; x++)
7031 if (level->encoding_16bit_field)
7032 chunk_size += putFile16BitBE(file, level->field[x][y]);
7034 chunk_size += putFile8Bit(file, level->field[x][y]);
7040 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7045 for (y = 0; y < level->fieldy; y++)
7046 for (x = 0; x < level->fieldx; x++)
7047 chunk_size += putFile16BitBE(file, level->field[x][y]);
7052 #if ENABLE_HISTORIC_CHUNKS
7053 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7057 putFile8Bit(file, EL_YAMYAM);
7058 putFile8Bit(file, level->num_yamyam_contents);
7059 putFile8Bit(file, 0);
7060 putFile8Bit(file, 0);
7062 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7063 for (y = 0; y < 3; y++)
7064 for (x = 0; x < 3; x++)
7065 if (level->encoding_16bit_field)
7066 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7068 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7072 #if ENABLE_HISTORIC_CHUNKS
7073 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7076 int num_contents, content_xsize, content_ysize;
7077 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7079 if (element == EL_YAMYAM)
7081 num_contents = level->num_yamyam_contents;
7085 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7086 for (y = 0; y < 3; y++)
7087 for (x = 0; x < 3; x++)
7088 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7090 else if (element == EL_BD_AMOEBA)
7096 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7097 for (y = 0; y < 3; y++)
7098 for (x = 0; x < 3; x++)
7099 content_array[i][x][y] = EL_EMPTY;
7100 content_array[0][0][0] = level->amoeba_content;
7104 // chunk header already written -- write empty chunk data
7105 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7107 Warn("cannot save content for element '%d'", element);
7112 putFile16BitBE(file, element);
7113 putFile8Bit(file, num_contents);
7114 putFile8Bit(file, content_xsize);
7115 putFile8Bit(file, content_ysize);
7117 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7119 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7120 for (y = 0; y < 3; y++)
7121 for (x = 0; x < 3; x++)
7122 putFile16BitBE(file, content_array[i][x][y]);
7126 #if ENABLE_HISTORIC_CHUNKS
7127 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7129 int envelope_nr = element - EL_ENVELOPE_1;
7130 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7134 chunk_size += putFile16BitBE(file, element);
7135 chunk_size += putFile16BitBE(file, envelope_len);
7136 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7137 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7139 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7140 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7142 for (i = 0; i < envelope_len; i++)
7143 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7149 #if ENABLE_HISTORIC_CHUNKS
7150 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7151 int num_changed_custom_elements)
7155 putFile16BitBE(file, num_changed_custom_elements);
7157 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7159 int element = EL_CUSTOM_START + i;
7161 struct ElementInfo *ei = &element_info[element];
7163 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7165 if (check < num_changed_custom_elements)
7167 putFile16BitBE(file, element);
7168 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7175 if (check != num_changed_custom_elements) // should not happen
7176 Warn("inconsistent number of custom element properties");
7180 #if ENABLE_HISTORIC_CHUNKS
7181 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7182 int num_changed_custom_elements)
7186 putFile16BitBE(file, num_changed_custom_elements);
7188 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7190 int element = EL_CUSTOM_START + i;
7192 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7194 if (check < num_changed_custom_elements)
7196 putFile16BitBE(file, element);
7197 putFile16BitBE(file, element_info[element].change->target_element);
7204 if (check != num_changed_custom_elements) // should not happen
7205 Warn("inconsistent number of custom target elements");
7209 #if ENABLE_HISTORIC_CHUNKS
7210 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7211 int num_changed_custom_elements)
7213 int i, j, x, y, check = 0;
7215 putFile16BitBE(file, num_changed_custom_elements);
7217 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7219 int element = EL_CUSTOM_START + i;
7220 struct ElementInfo *ei = &element_info[element];
7222 if (ei->modified_settings)
7224 if (check < num_changed_custom_elements)
7226 putFile16BitBE(file, element);
7228 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7229 putFile8Bit(file, ei->description[j]);
7231 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7233 // some free bytes for future properties and padding
7234 WriteUnusedBytesToFile(file, 7);
7236 putFile8Bit(file, ei->use_gfx_element);
7237 putFile16BitBE(file, ei->gfx_element_initial);
7239 putFile8Bit(file, ei->collect_score_initial);
7240 putFile8Bit(file, ei->collect_count_initial);
7242 putFile16BitBE(file, ei->push_delay_fixed);
7243 putFile16BitBE(file, ei->push_delay_random);
7244 putFile16BitBE(file, ei->move_delay_fixed);
7245 putFile16BitBE(file, ei->move_delay_random);
7247 putFile16BitBE(file, ei->move_pattern);
7248 putFile8Bit(file, ei->move_direction_initial);
7249 putFile8Bit(file, ei->move_stepsize);
7251 for (y = 0; y < 3; y++)
7252 for (x = 0; x < 3; x++)
7253 putFile16BitBE(file, ei->content.e[x][y]);
7255 putFile32BitBE(file, ei->change->events);
7257 putFile16BitBE(file, ei->change->target_element);
7259 putFile16BitBE(file, ei->change->delay_fixed);
7260 putFile16BitBE(file, ei->change->delay_random);
7261 putFile16BitBE(file, ei->change->delay_frames);
7263 putFile16BitBE(file, ei->change->initial_trigger_element);
7265 putFile8Bit(file, ei->change->explode);
7266 putFile8Bit(file, ei->change->use_target_content);
7267 putFile8Bit(file, ei->change->only_if_complete);
7268 putFile8Bit(file, ei->change->use_random_replace);
7270 putFile8Bit(file, ei->change->random_percentage);
7271 putFile8Bit(file, ei->change->replace_when);
7273 for (y = 0; y < 3; y++)
7274 for (x = 0; x < 3; x++)
7275 putFile16BitBE(file, ei->change->content.e[x][y]);
7277 putFile8Bit(file, ei->slippery_type);
7279 // some free bytes for future properties and padding
7280 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7287 if (check != num_changed_custom_elements) // should not happen
7288 Warn("inconsistent number of custom element properties");
7292 #if ENABLE_HISTORIC_CHUNKS
7293 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7295 struct ElementInfo *ei = &element_info[element];
7298 // ---------- custom element base property values (96 bytes) ----------------
7300 putFile16BitBE(file, element);
7302 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7303 putFile8Bit(file, ei->description[i]);
7305 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7307 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7309 putFile8Bit(file, ei->num_change_pages);
7311 putFile16BitBE(file, ei->ce_value_fixed_initial);
7312 putFile16BitBE(file, ei->ce_value_random_initial);
7313 putFile8Bit(file, ei->use_last_ce_value);
7315 putFile8Bit(file, ei->use_gfx_element);
7316 putFile16BitBE(file, ei->gfx_element_initial);
7318 putFile8Bit(file, ei->collect_score_initial);
7319 putFile8Bit(file, ei->collect_count_initial);
7321 putFile8Bit(file, ei->drop_delay_fixed);
7322 putFile8Bit(file, ei->push_delay_fixed);
7323 putFile8Bit(file, ei->drop_delay_random);
7324 putFile8Bit(file, ei->push_delay_random);
7325 putFile16BitBE(file, ei->move_delay_fixed);
7326 putFile16BitBE(file, ei->move_delay_random);
7328 // bits 0 - 15 of "move_pattern" ...
7329 putFile16BitBE(file, ei->move_pattern & 0xffff);
7330 putFile8Bit(file, ei->move_direction_initial);
7331 putFile8Bit(file, ei->move_stepsize);
7333 putFile8Bit(file, ei->slippery_type);
7335 for (y = 0; y < 3; y++)
7336 for (x = 0; x < 3; x++)
7337 putFile16BitBE(file, ei->content.e[x][y]);
7339 putFile16BitBE(file, ei->move_enter_element);
7340 putFile16BitBE(file, ei->move_leave_element);
7341 putFile8Bit(file, ei->move_leave_type);
7343 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7344 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7346 putFile8Bit(file, ei->access_direction);
7348 putFile8Bit(file, ei->explosion_delay);
7349 putFile8Bit(file, ei->ignition_delay);
7350 putFile8Bit(file, ei->explosion_type);
7352 // some free bytes for future custom property values and padding
7353 WriteUnusedBytesToFile(file, 1);
7355 // ---------- change page property values (48 bytes) ------------------------
7357 for (i = 0; i < ei->num_change_pages; i++)
7359 struct ElementChangeInfo *change = &ei->change_page[i];
7360 unsigned int event_bits;
7362 // bits 0 - 31 of "has_event[]" ...
7364 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7365 if (change->has_event[j])
7366 event_bits |= (1u << j);
7367 putFile32BitBE(file, event_bits);
7369 putFile16BitBE(file, change->target_element);
7371 putFile16BitBE(file, change->delay_fixed);
7372 putFile16BitBE(file, change->delay_random);
7373 putFile16BitBE(file, change->delay_frames);
7375 putFile16BitBE(file, change->initial_trigger_element);
7377 putFile8Bit(file, change->explode);
7378 putFile8Bit(file, change->use_target_content);
7379 putFile8Bit(file, change->only_if_complete);
7380 putFile8Bit(file, change->use_random_replace);
7382 putFile8Bit(file, change->random_percentage);
7383 putFile8Bit(file, change->replace_when);
7385 for (y = 0; y < 3; y++)
7386 for (x = 0; x < 3; x++)
7387 putFile16BitBE(file, change->target_content.e[x][y]);
7389 putFile8Bit(file, change->can_change);
7391 putFile8Bit(file, change->trigger_side);
7393 putFile8Bit(file, change->trigger_player);
7394 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7395 log_2(change->trigger_page)));
7397 putFile8Bit(file, change->has_action);
7398 putFile8Bit(file, change->action_type);
7399 putFile8Bit(file, change->action_mode);
7400 putFile16BitBE(file, change->action_arg);
7402 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7404 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7405 if (change->has_event[j])
7406 event_bits |= (1u << (j - 32));
7407 putFile8Bit(file, event_bits);
7412 #if ENABLE_HISTORIC_CHUNKS
7413 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7415 struct ElementInfo *ei = &element_info[element];
7416 struct ElementGroupInfo *group = ei->group;
7419 putFile16BitBE(file, element);
7421 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7422 putFile8Bit(file, ei->description[i]);
7424 putFile8Bit(file, group->num_elements);
7426 putFile8Bit(file, ei->use_gfx_element);
7427 putFile16BitBE(file, ei->gfx_element_initial);
7429 putFile8Bit(file, group->choice_mode);
7431 // some free bytes for future values and padding
7432 WriteUnusedBytesToFile(file, 3);
7434 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7435 putFile16BitBE(file, group->element[i]);
7439 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7440 boolean write_element)
7442 int save_type = entry->save_type;
7443 int data_type = entry->data_type;
7444 int conf_type = entry->conf_type;
7445 int byte_mask = conf_type & CONF_MASK_BYTES;
7446 int element = entry->element;
7447 int default_value = entry->default_value;
7449 boolean modified = FALSE;
7451 if (byte_mask != CONF_MASK_MULTI_BYTES)
7453 void *value_ptr = entry->value;
7454 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7457 // check if any settings have been modified before saving them
7458 if (value != default_value)
7461 // do not save if explicitly told or if unmodified default settings
7462 if ((save_type == SAVE_CONF_NEVER) ||
7463 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7467 num_bytes += putFile16BitBE(file, element);
7469 num_bytes += putFile8Bit(file, conf_type);
7470 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7471 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7472 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7475 else if (data_type == TYPE_STRING)
7477 char *default_string = entry->default_string;
7478 char *string = (char *)(entry->value);
7479 int string_length = strlen(string);
7482 // check if any settings have been modified before saving them
7483 if (!strEqual(string, default_string))
7486 // do not save if explicitly told or if unmodified default settings
7487 if ((save_type == SAVE_CONF_NEVER) ||
7488 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7492 num_bytes += putFile16BitBE(file, element);
7494 num_bytes += putFile8Bit(file, conf_type);
7495 num_bytes += putFile16BitBE(file, string_length);
7497 for (i = 0; i < string_length; i++)
7498 num_bytes += putFile8Bit(file, string[i]);
7500 else if (data_type == TYPE_ELEMENT_LIST)
7502 int *element_array = (int *)(entry->value);
7503 int num_elements = *(int *)(entry->num_entities);
7506 // check if any settings have been modified before saving them
7507 for (i = 0; i < num_elements; i++)
7508 if (element_array[i] != default_value)
7511 // do not save if explicitly told or if unmodified default settings
7512 if ((save_type == SAVE_CONF_NEVER) ||
7513 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7517 num_bytes += putFile16BitBE(file, element);
7519 num_bytes += putFile8Bit(file, conf_type);
7520 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7522 for (i = 0; i < num_elements; i++)
7523 num_bytes += putFile16BitBE(file, element_array[i]);
7525 else if (data_type == TYPE_CONTENT_LIST)
7527 struct Content *content = (struct Content *)(entry->value);
7528 int num_contents = *(int *)(entry->num_entities);
7531 // check if any settings have been modified before saving them
7532 for (i = 0; i < num_contents; i++)
7533 for (y = 0; y < 3; y++)
7534 for (x = 0; x < 3; x++)
7535 if (content[i].e[x][y] != default_value)
7538 // do not save if explicitly told or if unmodified default settings
7539 if ((save_type == SAVE_CONF_NEVER) ||
7540 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7544 num_bytes += putFile16BitBE(file, element);
7546 num_bytes += putFile8Bit(file, conf_type);
7547 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7549 for (i = 0; i < num_contents; i++)
7550 for (y = 0; y < 3; y++)
7551 for (x = 0; x < 3; x++)
7552 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7558 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7563 li = *level; // copy level data into temporary buffer
7565 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7566 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7571 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7576 li = *level; // copy level data into temporary buffer
7578 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7579 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7584 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7586 int envelope_nr = element - EL_ENVELOPE_1;
7590 chunk_size += putFile16BitBE(file, element);
7592 // copy envelope data into temporary buffer
7593 xx_envelope = level->envelope[envelope_nr];
7595 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7596 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7601 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7603 struct ElementInfo *ei = &element_info[element];
7607 chunk_size += putFile16BitBE(file, element);
7609 xx_ei = *ei; // copy element data into temporary buffer
7611 // set default description string for this specific element
7612 strcpy(xx_default_description, getDefaultElementDescription(ei));
7614 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7615 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7617 for (i = 0; i < ei->num_change_pages; i++)
7619 struct ElementChangeInfo *change = &ei->change_page[i];
7621 xx_current_change_page = i;
7623 xx_change = *change; // copy change data into temporary buffer
7626 setEventBitsFromEventFlags(change);
7628 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7629 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7636 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7638 struct ElementInfo *ei = &element_info[element];
7639 struct ElementGroupInfo *group = ei->group;
7643 chunk_size += putFile16BitBE(file, element);
7645 xx_ei = *ei; // copy element data into temporary buffer
7646 xx_group = *group; // copy group data into temporary buffer
7648 // set default description string for this specific element
7649 strcpy(xx_default_description, getDefaultElementDescription(ei));
7651 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7652 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7657 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7659 struct ElementInfo *ei = &element_info[element];
7663 chunk_size += putFile16BitBE(file, element);
7665 xx_ei = *ei; // copy element data into temporary buffer
7667 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7668 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7673 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7674 boolean save_as_template)
7680 if (!(file = fopen(filename, MODE_WRITE)))
7682 Warn("cannot save level file '%s'", filename);
7687 level->file_version = FILE_VERSION_ACTUAL;
7688 level->game_version = GAME_VERSION_ACTUAL;
7690 level->creation_date = getCurrentDate();
7692 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7693 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7695 chunk_size = SaveLevel_VERS(NULL, level);
7696 putFileChunkBE(file, "VERS", chunk_size);
7697 SaveLevel_VERS(file, level);
7699 chunk_size = SaveLevel_DATE(NULL, level);
7700 putFileChunkBE(file, "DATE", chunk_size);
7701 SaveLevel_DATE(file, level);
7703 chunk_size = SaveLevel_NAME(NULL, level);
7704 putFileChunkBE(file, "NAME", chunk_size);
7705 SaveLevel_NAME(file, level);
7707 chunk_size = SaveLevel_AUTH(NULL, level);
7708 putFileChunkBE(file, "AUTH", chunk_size);
7709 SaveLevel_AUTH(file, level);
7711 chunk_size = SaveLevel_INFO(NULL, level);
7712 putFileChunkBE(file, "INFO", chunk_size);
7713 SaveLevel_INFO(file, level);
7715 chunk_size = SaveLevel_BODY(NULL, level);
7716 putFileChunkBE(file, "BODY", chunk_size);
7717 SaveLevel_BODY(file, level);
7719 chunk_size = SaveLevel_ELEM(NULL, level);
7720 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7722 putFileChunkBE(file, "ELEM", chunk_size);
7723 SaveLevel_ELEM(file, level);
7726 for (i = 0; i < NUM_ENVELOPES; i++)
7728 int element = EL_ENVELOPE_1 + i;
7730 chunk_size = SaveLevel_NOTE(NULL, level, element);
7731 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7733 putFileChunkBE(file, "NOTE", chunk_size);
7734 SaveLevel_NOTE(file, level, element);
7738 // if not using template level, check for non-default custom/group elements
7739 if (!level->use_custom_template || save_as_template)
7741 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7743 int element = EL_CUSTOM_START + i;
7745 chunk_size = SaveLevel_CUSX(NULL, level, element);
7746 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7748 putFileChunkBE(file, "CUSX", chunk_size);
7749 SaveLevel_CUSX(file, level, element);
7753 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7755 int element = EL_GROUP_START + i;
7757 chunk_size = SaveLevel_GRPX(NULL, level, element);
7758 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7760 putFileChunkBE(file, "GRPX", chunk_size);
7761 SaveLevel_GRPX(file, level, element);
7765 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7767 int element = GET_EMPTY_ELEMENT(i);
7769 chunk_size = SaveLevel_EMPX(NULL, level, element);
7770 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7772 putFileChunkBE(file, "EMPX", chunk_size);
7773 SaveLevel_EMPX(file, level, element);
7780 SetFilePermissions(filename, PERMS_PRIVATE);
7783 void SaveLevel(int nr)
7785 char *filename = getDefaultLevelFilename(nr);
7787 SaveLevelFromFilename(&level, filename, FALSE);
7790 void SaveLevelTemplate(void)
7792 char *filename = getLocalLevelTemplateFilename();
7794 SaveLevelFromFilename(&level, filename, TRUE);
7797 boolean SaveLevelChecked(int nr)
7799 char *filename = getDefaultLevelFilename(nr);
7800 boolean new_level = !fileExists(filename);
7801 boolean level_saved = FALSE;
7803 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7808 Request("Level saved!", REQ_CONFIRM);
7816 void DumpLevel(struct LevelInfo *level)
7818 if (level->no_level_file || level->no_valid_file)
7820 Warn("cannot dump -- no valid level file found");
7826 Print("Level xxx (file version %08d, game version %08d)\n",
7827 level->file_version, level->game_version);
7830 Print("Level author: '%s'\n", level->author);
7831 Print("Level title: '%s'\n", level->name);
7833 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7835 Print("Level time: %d seconds\n", level->time);
7836 Print("Gems needed: %d\n", level->gems_needed);
7838 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7839 Print("Time for wheel: %d seconds\n", level->time_wheel);
7840 Print("Time for light: %d seconds\n", level->time_light);
7841 Print("Time for timegate: %d seconds\n", level->time_timegate);
7843 Print("Amoeba speed: %d\n", level->amoeba_speed);
7846 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7847 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7848 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7849 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7850 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7851 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7857 for (i = 0; i < NUM_ENVELOPES; i++)
7859 char *text = level->envelope[i].text;
7860 int text_len = strlen(text);
7861 boolean has_text = FALSE;
7863 for (j = 0; j < text_len; j++)
7864 if (text[j] != ' ' && text[j] != '\n')
7870 Print("Envelope %d:\n'%s'\n", i + 1, text);
7878 void DumpLevels(void)
7880 static LevelDirTree *dumplevel_leveldir = NULL;
7882 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7883 global.dumplevel_leveldir);
7885 if (dumplevel_leveldir == NULL)
7886 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7888 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7889 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7890 Fail("no such level number: %d", global.dumplevel_level_nr);
7892 leveldir_current = dumplevel_leveldir;
7894 LoadLevel(global.dumplevel_level_nr);
7901 // ============================================================================
7902 // tape file functions
7903 // ============================================================================
7905 static void setTapeInfoToDefaults(void)
7909 // always start with reliable default values (empty tape)
7912 // default values (also for pre-1.2 tapes) with only the first player
7913 tape.player_participates[0] = TRUE;
7914 for (i = 1; i < MAX_PLAYERS; i++)
7915 tape.player_participates[i] = FALSE;
7917 // at least one (default: the first) player participates in every tape
7918 tape.num_participating_players = 1;
7920 tape.property_bits = TAPE_PROPERTY_NONE;
7922 tape.level_nr = level_nr;
7924 tape.changed = FALSE;
7925 tape.solved = FALSE;
7927 tape.recording = FALSE;
7928 tape.playing = FALSE;
7929 tape.pausing = FALSE;
7931 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7932 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7934 tape.no_info_chunk = TRUE;
7935 tape.no_valid_file = FALSE;
7938 static int getTapePosSize(struct TapeInfo *tape)
7940 int tape_pos_size = 0;
7942 if (tape->use_key_actions)
7943 tape_pos_size += tape->num_participating_players;
7945 if (tape->use_mouse_actions)
7946 tape_pos_size += 3; // x and y position and mouse button mask
7948 tape_pos_size += 1; // tape action delay value
7950 return tape_pos_size;
7953 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7955 tape->use_key_actions = FALSE;
7956 tape->use_mouse_actions = FALSE;
7958 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7959 tape->use_key_actions = TRUE;
7961 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7962 tape->use_mouse_actions = TRUE;
7965 static int getTapeActionValue(struct TapeInfo *tape)
7967 return (tape->use_key_actions &&
7968 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7969 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7970 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7971 TAPE_ACTIONS_DEFAULT);
7974 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7976 tape->file_version = getFileVersion(file);
7977 tape->game_version = getFileVersion(file);
7982 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7986 tape->random_seed = getFile32BitBE(file);
7987 tape->date = getFile32BitBE(file);
7988 tape->length = getFile32BitBE(file);
7990 // read header fields that are new since version 1.2
7991 if (tape->file_version >= FILE_VERSION_1_2)
7993 byte store_participating_players = getFile8Bit(file);
7996 // since version 1.2, tapes store which players participate in the tape
7997 tape->num_participating_players = 0;
7998 for (i = 0; i < MAX_PLAYERS; i++)
8000 tape->player_participates[i] = FALSE;
8002 if (store_participating_players & (1 << i))
8004 tape->player_participates[i] = TRUE;
8005 tape->num_participating_players++;
8009 setTapeActionFlags(tape, getFile8Bit(file));
8011 tape->property_bits = getFile8Bit(file);
8012 tape->solved = getFile8Bit(file);
8014 engine_version = getFileVersion(file);
8015 if (engine_version > 0)
8016 tape->engine_version = engine_version;
8018 tape->engine_version = tape->game_version;
8024 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8026 tape->scr_fieldx = getFile8Bit(file);
8027 tape->scr_fieldy = getFile8Bit(file);
8032 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8034 char *level_identifier = NULL;
8035 int level_identifier_size;
8038 tape->no_info_chunk = FALSE;
8040 level_identifier_size = getFile16BitBE(file);
8042 level_identifier = checked_malloc(level_identifier_size);
8044 for (i = 0; i < level_identifier_size; i++)
8045 level_identifier[i] = getFile8Bit(file);
8047 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8048 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8050 checked_free(level_identifier);
8052 tape->level_nr = getFile16BitBE(file);
8054 chunk_size = 2 + level_identifier_size + 2;
8059 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8062 int tape_pos_size = getTapePosSize(tape);
8063 int chunk_size_expected = tape_pos_size * tape->length;
8065 if (chunk_size_expected != chunk_size)
8067 ReadUnusedBytesFromFile(file, chunk_size);
8068 return chunk_size_expected;
8071 for (i = 0; i < tape->length; i++)
8073 if (i >= MAX_TAPE_LEN)
8075 Warn("tape truncated -- size exceeds maximum tape size %d",
8078 // tape too large; read and ignore remaining tape data from this chunk
8079 for (;i < tape->length; i++)
8080 ReadUnusedBytesFromFile(file, tape_pos_size);
8085 if (tape->use_key_actions)
8087 for (j = 0; j < MAX_PLAYERS; j++)
8089 tape->pos[i].action[j] = MV_NONE;
8091 if (tape->player_participates[j])
8092 tape->pos[i].action[j] = getFile8Bit(file);
8096 if (tape->use_mouse_actions)
8098 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8099 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8100 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8103 tape->pos[i].delay = getFile8Bit(file);
8105 if (tape->file_version == FILE_VERSION_1_0)
8107 // eliminate possible diagonal moves in old tapes
8108 // this is only for backward compatibility
8110 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8111 byte action = tape->pos[i].action[0];
8112 int k, num_moves = 0;
8114 for (k = 0; k<4; k++)
8116 if (action & joy_dir[k])
8118 tape->pos[i + num_moves].action[0] = joy_dir[k];
8120 tape->pos[i + num_moves].delay = 0;
8129 tape->length += num_moves;
8132 else if (tape->file_version < FILE_VERSION_2_0)
8134 // convert pre-2.0 tapes to new tape format
8136 if (tape->pos[i].delay > 1)
8139 tape->pos[i + 1] = tape->pos[i];
8140 tape->pos[i + 1].delay = 1;
8143 for (j = 0; j < MAX_PLAYERS; j++)
8144 tape->pos[i].action[j] = MV_NONE;
8145 tape->pos[i].delay--;
8152 if (checkEndOfFile(file))
8156 if (i != tape->length)
8157 chunk_size = tape_pos_size * i;
8162 static void LoadTape_SokobanSolution(char *filename)
8165 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8167 if (!(file = openFile(filename, MODE_READ)))
8169 tape.no_valid_file = TRUE;
8174 while (!checkEndOfFile(file))
8176 unsigned char c = getByteFromFile(file);
8178 if (checkEndOfFile(file))
8185 tape.pos[tape.length].action[0] = MV_UP;
8186 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8192 tape.pos[tape.length].action[0] = MV_DOWN;
8193 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8199 tape.pos[tape.length].action[0] = MV_LEFT;
8200 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8206 tape.pos[tape.length].action[0] = MV_RIGHT;
8207 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8215 // ignore white-space characters
8219 tape.no_valid_file = TRUE;
8221 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8229 if (tape.no_valid_file)
8232 tape.length_frames = GetTapeLengthFrames();
8233 tape.length_seconds = GetTapeLengthSeconds();
8236 void LoadTapeFromFilename(char *filename)
8238 char cookie[MAX_LINE_LEN];
8239 char chunk_name[CHUNK_ID_LEN + 1];
8243 // always start with reliable default values
8244 setTapeInfoToDefaults();
8246 if (strSuffix(filename, ".sln"))
8248 LoadTape_SokobanSolution(filename);
8253 if (!(file = openFile(filename, MODE_READ)))
8255 tape.no_valid_file = TRUE;
8260 getFileChunkBE(file, chunk_name, NULL);
8261 if (strEqual(chunk_name, "RND1"))
8263 getFile32BitBE(file); // not used
8265 getFileChunkBE(file, chunk_name, NULL);
8266 if (!strEqual(chunk_name, "TAPE"))
8268 tape.no_valid_file = TRUE;
8270 Warn("unknown format of tape file '%s'", filename);
8277 else // check for pre-2.0 file format with cookie string
8279 strcpy(cookie, chunk_name);
8280 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8282 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8283 cookie[strlen(cookie) - 1] = '\0';
8285 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8287 tape.no_valid_file = TRUE;
8289 Warn("unknown format of tape file '%s'", filename);
8296 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8298 tape.no_valid_file = TRUE;
8300 Warn("unsupported version of tape file '%s'", filename);
8307 // pre-2.0 tape files have no game version, so use file version here
8308 tape.game_version = tape.file_version;
8311 if (tape.file_version < FILE_VERSION_1_2)
8313 // tape files from versions before 1.2.0 without chunk structure
8314 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8315 LoadTape_BODY(file, 2 * tape.length, &tape);
8323 int (*loader)(File *, int, struct TapeInfo *);
8327 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8328 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8329 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8330 { "INFO", -1, LoadTape_INFO },
8331 { "BODY", -1, LoadTape_BODY },
8335 while (getFileChunkBE(file, chunk_name, &chunk_size))
8339 while (chunk_info[i].name != NULL &&
8340 !strEqual(chunk_name, chunk_info[i].name))
8343 if (chunk_info[i].name == NULL)
8345 Warn("unknown chunk '%s' in tape file '%s'",
8346 chunk_name, filename);
8348 ReadUnusedBytesFromFile(file, chunk_size);
8350 else if (chunk_info[i].size != -1 &&
8351 chunk_info[i].size != chunk_size)
8353 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8354 chunk_size, chunk_name, filename);
8356 ReadUnusedBytesFromFile(file, chunk_size);
8360 // call function to load this tape chunk
8361 int chunk_size_expected =
8362 (chunk_info[i].loader)(file, chunk_size, &tape);
8364 // the size of some chunks cannot be checked before reading other
8365 // chunks first (like "HEAD" and "BODY") that contain some header
8366 // information, so check them here
8367 if (chunk_size_expected != chunk_size)
8369 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8370 chunk_size, chunk_name, filename);
8378 tape.length_frames = GetTapeLengthFrames();
8379 tape.length_seconds = GetTapeLengthSeconds();
8382 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8384 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8386 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8387 tape.engine_version);
8391 void LoadTape(int nr)
8393 char *filename = getTapeFilename(nr);
8395 LoadTapeFromFilename(filename);
8398 void LoadSolutionTape(int nr)
8400 char *filename = getSolutionTapeFilename(nr);
8402 LoadTapeFromFilename(filename);
8404 if (TAPE_IS_EMPTY(tape) &&
8405 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8406 level.native_sp_level->demo.is_available)
8407 CopyNativeTape_SP_to_RND(&level);
8410 void LoadScoreTape(char *score_tape_basename, int nr)
8412 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8414 LoadTapeFromFilename(filename);
8417 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8419 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8421 LoadTapeFromFilename(filename);
8424 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8426 // chunk required for team mode tapes with non-default screen size
8427 return (tape->num_participating_players > 1 &&
8428 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8429 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8432 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8434 putFileVersion(file, tape->file_version);
8435 putFileVersion(file, tape->game_version);
8438 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8441 byte store_participating_players = 0;
8443 // set bits for participating players for compact storage
8444 for (i = 0; i < MAX_PLAYERS; i++)
8445 if (tape->player_participates[i])
8446 store_participating_players |= (1 << i);
8448 putFile32BitBE(file, tape->random_seed);
8449 putFile32BitBE(file, tape->date);
8450 putFile32BitBE(file, tape->length);
8452 putFile8Bit(file, store_participating_players);
8454 putFile8Bit(file, getTapeActionValue(tape));
8456 putFile8Bit(file, tape->property_bits);
8457 putFile8Bit(file, tape->solved);
8459 putFileVersion(file, tape->engine_version);
8462 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8464 putFile8Bit(file, tape->scr_fieldx);
8465 putFile8Bit(file, tape->scr_fieldy);
8468 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8470 int level_identifier_size = strlen(tape->level_identifier) + 1;
8473 putFile16BitBE(file, level_identifier_size);
8475 for (i = 0; i < level_identifier_size; i++)
8476 putFile8Bit(file, tape->level_identifier[i]);
8478 putFile16BitBE(file, tape->level_nr);
8481 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8485 for (i = 0; i < tape->length; i++)
8487 if (tape->use_key_actions)
8489 for (j = 0; j < MAX_PLAYERS; j++)
8490 if (tape->player_participates[j])
8491 putFile8Bit(file, tape->pos[i].action[j]);
8494 if (tape->use_mouse_actions)
8496 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8497 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8498 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8501 putFile8Bit(file, tape->pos[i].delay);
8505 void SaveTapeToFilename(char *filename)
8509 int info_chunk_size;
8510 int body_chunk_size;
8512 if (!(file = fopen(filename, MODE_WRITE)))
8514 Warn("cannot save level recording file '%s'", filename);
8519 tape_pos_size = getTapePosSize(&tape);
8521 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8522 body_chunk_size = tape_pos_size * tape.length;
8524 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8525 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8527 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8528 SaveTape_VERS(file, &tape);
8530 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8531 SaveTape_HEAD(file, &tape);
8533 if (checkSaveTape_SCRN(&tape))
8535 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8536 SaveTape_SCRN(file, &tape);
8539 putFileChunkBE(file, "INFO", info_chunk_size);
8540 SaveTape_INFO(file, &tape);
8542 putFileChunkBE(file, "BODY", body_chunk_size);
8543 SaveTape_BODY(file, &tape);
8547 SetFilePermissions(filename, PERMS_PRIVATE);
8550 static void SaveTapeExt(char *filename)
8554 tape.file_version = FILE_VERSION_ACTUAL;
8555 tape.game_version = GAME_VERSION_ACTUAL;
8557 tape.num_participating_players = 0;
8559 // count number of participating players
8560 for (i = 0; i < MAX_PLAYERS; i++)
8561 if (tape.player_participates[i])
8562 tape.num_participating_players++;
8564 SaveTapeToFilename(filename);
8566 tape.changed = FALSE;
8569 void SaveTape(int nr)
8571 char *filename = getTapeFilename(nr);
8573 InitTapeDirectory(leveldir_current->subdir);
8575 SaveTapeExt(filename);
8578 void SaveScoreTape(int nr)
8580 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8582 // used instead of "leveldir_current->subdir" (for network games)
8583 InitScoreTapeDirectory(levelset.identifier, nr);
8585 SaveTapeExt(filename);
8588 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8589 unsigned int req_state_added)
8591 char *filename = getTapeFilename(nr);
8592 boolean new_tape = !fileExists(filename);
8593 boolean tape_saved = FALSE;
8595 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8600 Request(msg_saved, REQ_CONFIRM | req_state_added);
8608 boolean SaveTapeChecked(int nr)
8610 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8613 boolean SaveTapeChecked_LevelSolved(int nr)
8615 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8616 "Level solved! Tape saved!", REQ_STAY_OPEN);
8619 void DumpTape(struct TapeInfo *tape)
8621 int tape_frame_counter;
8624 if (tape->no_valid_file)
8626 Warn("cannot dump -- no valid tape file found");
8633 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8634 tape->level_nr, tape->file_version, tape->game_version);
8635 Print(" (effective engine version %08d)\n",
8636 tape->engine_version);
8637 Print("Level series identifier: '%s'\n", tape->level_identifier);
8639 Print("Solution tape: %s\n",
8640 tape->solved ? "yes" :
8641 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8643 Print("Special tape properties: ");
8644 if (tape->property_bits == TAPE_PROPERTY_NONE)
8646 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8647 Print("[em_random_bug]");
8648 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8649 Print("[game_speed]");
8650 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8652 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8653 Print("[single_step]");
8654 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8655 Print("[snapshot]");
8656 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8657 Print("[replayed]");
8658 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8659 Print("[tas_keys]");
8660 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8661 Print("[small_graphics]");
8664 int year2 = tape->date / 10000;
8665 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8666 int month_index_raw = (tape->date / 100) % 100;
8667 int month_index = month_index_raw % 12; // prevent invalid index
8668 int month = month_index + 1;
8669 int day = tape->date % 100;
8671 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8675 tape_frame_counter = 0;
8677 for (i = 0; i < tape->length; i++)
8679 if (i >= MAX_TAPE_LEN)
8684 for (j = 0; j < MAX_PLAYERS; j++)
8686 if (tape->player_participates[j])
8688 int action = tape->pos[i].action[j];
8690 Print("%d:%02x ", j, action);
8691 Print("[%c%c%c%c|%c%c] - ",
8692 (action & JOY_LEFT ? '<' : ' '),
8693 (action & JOY_RIGHT ? '>' : ' '),
8694 (action & JOY_UP ? '^' : ' '),
8695 (action & JOY_DOWN ? 'v' : ' '),
8696 (action & JOY_BUTTON_1 ? '1' : ' '),
8697 (action & JOY_BUTTON_2 ? '2' : ' '));
8701 Print("(%03d) ", tape->pos[i].delay);
8702 Print("[%05d]\n", tape_frame_counter);
8704 tape_frame_counter += tape->pos[i].delay;
8710 void DumpTapes(void)
8712 static LevelDirTree *dumptape_leveldir = NULL;
8714 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8715 global.dumptape_leveldir);
8717 if (dumptape_leveldir == NULL)
8718 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8720 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8721 global.dumptape_level_nr > dumptape_leveldir->last_level)
8722 Fail("no such level number: %d", global.dumptape_level_nr);
8724 leveldir_current = dumptape_leveldir;
8726 if (options.mytapes)
8727 LoadTape(global.dumptape_level_nr);
8729 LoadSolutionTape(global.dumptape_level_nr);
8737 // ============================================================================
8738 // score file functions
8739 // ============================================================================
8741 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8745 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8747 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8748 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8749 scores->entry[i].score = 0;
8750 scores->entry[i].time = 0;
8752 scores->entry[i].id = -1;
8753 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8754 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8755 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8756 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8757 strcpy(scores->entry[i].country_code, "??");
8760 scores->num_entries = 0;
8761 scores->last_added = -1;
8762 scores->last_added_local = -1;
8764 scores->updated = FALSE;
8765 scores->uploaded = FALSE;
8766 scores->tape_downloaded = FALSE;
8767 scores->force_last_added = FALSE;
8769 // The following values are intentionally not reset here:
8773 // - continue_playing
8774 // - continue_on_return
8777 static void setScoreInfoToDefaults(void)
8779 setScoreInfoToDefaultsExt(&scores);
8782 static void setServerScoreInfoToDefaults(void)
8784 setScoreInfoToDefaultsExt(&server_scores);
8787 static void LoadScore_OLD(int nr)
8790 char *filename = getScoreFilename(nr);
8791 char cookie[MAX_LINE_LEN];
8792 char line[MAX_LINE_LEN];
8796 if (!(file = fopen(filename, MODE_READ)))
8799 // check file identifier
8800 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8802 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8803 cookie[strlen(cookie) - 1] = '\0';
8805 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8807 Warn("unknown format of score file '%s'", filename);
8814 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8816 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8817 Warn("fscanf() failed; %s", strerror(errno));
8819 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8822 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8823 line[strlen(line) - 1] = '\0';
8825 for (line_ptr = line; *line_ptr; line_ptr++)
8827 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8829 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8830 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8839 static void ConvertScore_OLD(void)
8841 // only convert score to time for levels that rate playing time over score
8842 if (!level.rate_time_over_score)
8845 // convert old score to playing time for score-less levels (like Supaplex)
8846 int time_final_max = 999;
8849 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8851 int score = scores.entry[i].score;
8853 if (score > 0 && score < time_final_max)
8854 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8858 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8860 scores->file_version = getFileVersion(file);
8861 scores->game_version = getFileVersion(file);
8866 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8868 char *level_identifier = NULL;
8869 int level_identifier_size;
8872 level_identifier_size = getFile16BitBE(file);
8874 level_identifier = checked_malloc(level_identifier_size);
8876 for (i = 0; i < level_identifier_size; i++)
8877 level_identifier[i] = getFile8Bit(file);
8879 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8880 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8882 checked_free(level_identifier);
8884 scores->level_nr = getFile16BitBE(file);
8885 scores->num_entries = getFile16BitBE(file);
8887 chunk_size = 2 + level_identifier_size + 2 + 2;
8892 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8896 for (i = 0; i < scores->num_entries; i++)
8898 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8899 scores->entry[i].name[j] = getFile8Bit(file);
8901 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8904 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8909 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8913 for (i = 0; i < scores->num_entries; i++)
8914 scores->entry[i].score = getFile16BitBE(file);
8916 chunk_size = scores->num_entries * 2;
8921 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8925 for (i = 0; i < scores->num_entries; i++)
8926 scores->entry[i].score = getFile32BitBE(file);
8928 chunk_size = scores->num_entries * 4;
8933 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8937 for (i = 0; i < scores->num_entries; i++)
8938 scores->entry[i].time = getFile32BitBE(file);
8940 chunk_size = scores->num_entries * 4;
8945 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8949 for (i = 0; i < scores->num_entries; i++)
8951 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8952 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8954 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8957 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8962 void LoadScore(int nr)
8964 char *filename = getScoreFilename(nr);
8965 char cookie[MAX_LINE_LEN];
8966 char chunk_name[CHUNK_ID_LEN + 1];
8968 boolean old_score_file_format = FALSE;
8971 // always start with reliable default values
8972 setScoreInfoToDefaults();
8974 if (!(file = openFile(filename, MODE_READ)))
8977 getFileChunkBE(file, chunk_name, NULL);
8978 if (strEqual(chunk_name, "RND1"))
8980 getFile32BitBE(file); // not used
8982 getFileChunkBE(file, chunk_name, NULL);
8983 if (!strEqual(chunk_name, "SCOR"))
8985 Warn("unknown format of score file '%s'", filename);
8992 else // check for old file format with cookie string
8994 strcpy(cookie, chunk_name);
8995 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8997 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8998 cookie[strlen(cookie) - 1] = '\0';
9000 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9002 Warn("unknown format of score file '%s'", filename);
9009 old_score_file_format = TRUE;
9012 if (old_score_file_format)
9014 // score files from versions before 4.2.4.0 without chunk structure
9017 // convert score to time, if possible (mainly for Supaplex levels)
9026 int (*loader)(File *, int, struct ScoreInfo *);
9030 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9031 { "INFO", -1, LoadScore_INFO },
9032 { "NAME", -1, LoadScore_NAME },
9033 { "SCOR", -1, LoadScore_SCOR },
9034 { "SC4R", -1, LoadScore_SC4R },
9035 { "TIME", -1, LoadScore_TIME },
9036 { "TAPE", -1, LoadScore_TAPE },
9041 while (getFileChunkBE(file, chunk_name, &chunk_size))
9045 while (chunk_info[i].name != NULL &&
9046 !strEqual(chunk_name, chunk_info[i].name))
9049 if (chunk_info[i].name == NULL)
9051 Warn("unknown chunk '%s' in score file '%s'",
9052 chunk_name, filename);
9054 ReadUnusedBytesFromFile(file, chunk_size);
9056 else if (chunk_info[i].size != -1 &&
9057 chunk_info[i].size != chunk_size)
9059 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9060 chunk_size, chunk_name, filename);
9062 ReadUnusedBytesFromFile(file, chunk_size);
9066 // call function to load this score chunk
9067 int chunk_size_expected =
9068 (chunk_info[i].loader)(file, chunk_size, &scores);
9070 // the size of some chunks cannot be checked before reading other
9071 // chunks first (like "HEAD" and "BODY") that contain some header
9072 // information, so check them here
9073 if (chunk_size_expected != chunk_size)
9075 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9076 chunk_size, chunk_name, filename);
9085 #if ENABLE_HISTORIC_CHUNKS
9086 void SaveScore_OLD(int nr)
9089 char *filename = getScoreFilename(nr);
9092 // used instead of "leveldir_current->subdir" (for network games)
9093 InitScoreDirectory(levelset.identifier);
9095 if (!(file = fopen(filename, MODE_WRITE)))
9097 Warn("cannot save score for level %d", nr);
9102 fprintf(file, "%s\n\n", SCORE_COOKIE);
9104 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9105 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9109 SetFilePermissions(filename, PERMS_PRIVATE);
9113 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9115 putFileVersion(file, scores->file_version);
9116 putFileVersion(file, scores->game_version);
9119 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9121 int level_identifier_size = strlen(scores->level_identifier) + 1;
9124 putFile16BitBE(file, level_identifier_size);
9126 for (i = 0; i < level_identifier_size; i++)
9127 putFile8Bit(file, scores->level_identifier[i]);
9129 putFile16BitBE(file, scores->level_nr);
9130 putFile16BitBE(file, scores->num_entries);
9133 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9137 for (i = 0; i < scores->num_entries; i++)
9139 int name_size = strlen(scores->entry[i].name);
9141 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9142 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9146 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9150 for (i = 0; i < scores->num_entries; i++)
9151 putFile16BitBE(file, scores->entry[i].score);
9154 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9158 for (i = 0; i < scores->num_entries; i++)
9159 putFile32BitBE(file, scores->entry[i].score);
9162 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9166 for (i = 0; i < scores->num_entries; i++)
9167 putFile32BitBE(file, scores->entry[i].time);
9170 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9174 for (i = 0; i < scores->num_entries; i++)
9176 int size = strlen(scores->entry[i].tape_basename);
9178 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9179 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9183 static void SaveScoreToFilename(char *filename)
9186 int info_chunk_size;
9187 int name_chunk_size;
9188 int scor_chunk_size;
9189 int sc4r_chunk_size;
9190 int time_chunk_size;
9191 int tape_chunk_size;
9192 boolean has_large_score_values;
9195 if (!(file = fopen(filename, MODE_WRITE)))
9197 Warn("cannot save score file '%s'", filename);
9202 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9203 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9204 scor_chunk_size = scores.num_entries * 2;
9205 sc4r_chunk_size = scores.num_entries * 4;
9206 time_chunk_size = scores.num_entries * 4;
9207 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9209 has_large_score_values = FALSE;
9210 for (i = 0; i < scores.num_entries; i++)
9211 if (scores.entry[i].score > 0xffff)
9212 has_large_score_values = TRUE;
9214 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9215 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9217 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9218 SaveScore_VERS(file, &scores);
9220 putFileChunkBE(file, "INFO", info_chunk_size);
9221 SaveScore_INFO(file, &scores);
9223 putFileChunkBE(file, "NAME", name_chunk_size);
9224 SaveScore_NAME(file, &scores);
9226 if (has_large_score_values)
9228 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9229 SaveScore_SC4R(file, &scores);
9233 putFileChunkBE(file, "SCOR", scor_chunk_size);
9234 SaveScore_SCOR(file, &scores);
9237 putFileChunkBE(file, "TIME", time_chunk_size);
9238 SaveScore_TIME(file, &scores);
9240 putFileChunkBE(file, "TAPE", tape_chunk_size);
9241 SaveScore_TAPE(file, &scores);
9245 SetFilePermissions(filename, PERMS_PRIVATE);
9248 void SaveScore(int nr)
9250 char *filename = getScoreFilename(nr);
9253 // used instead of "leveldir_current->subdir" (for network games)
9254 InitScoreDirectory(levelset.identifier);
9256 scores.file_version = FILE_VERSION_ACTUAL;
9257 scores.game_version = GAME_VERSION_ACTUAL;
9259 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9260 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9261 scores.level_nr = level_nr;
9263 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9264 if (scores.entry[i].score == 0 &&
9265 scores.entry[i].time == 0 &&
9266 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9269 scores.num_entries = i;
9271 if (scores.num_entries == 0)
9274 SaveScoreToFilename(filename);
9277 static void LoadServerScoreFromCache(int nr)
9279 struct ScoreEntry score_entry;
9288 { &score_entry.score, FALSE, 0 },
9289 { &score_entry.time, FALSE, 0 },
9290 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9291 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9292 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9293 { &score_entry.id, FALSE, 0 },
9294 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9295 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9296 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9297 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9301 char *filename = getScoreCacheFilename(nr);
9302 SetupFileHash *score_hash = loadSetupFileHash(filename);
9305 server_scores.num_entries = 0;
9307 if (score_hash == NULL)
9310 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9312 score_entry = server_scores.entry[i];
9314 for (j = 0; score_mapping[j].value != NULL; j++)
9318 sprintf(token, "%02d.%d", i, j);
9320 char *value = getHashEntry(score_hash, token);
9325 if (score_mapping[j].is_string)
9327 char *score_value = (char *)score_mapping[j].value;
9328 int value_size = score_mapping[j].string_size;
9330 strncpy(score_value, value, value_size);
9331 score_value[value_size] = '\0';
9335 int *score_value = (int *)score_mapping[j].value;
9337 *score_value = atoi(value);
9340 server_scores.num_entries = i + 1;
9343 server_scores.entry[i] = score_entry;
9346 freeSetupFileHash(score_hash);
9349 void LoadServerScore(int nr, boolean download_score)
9351 if (!setup.use_api_server)
9354 // always start with reliable default values
9355 setServerScoreInfoToDefaults();
9357 // 1st step: load server scores from cache file (which may not exist)
9358 // (this should prevent reading it while the thread is writing to it)
9359 LoadServerScoreFromCache(nr);
9361 if (download_score && runtime.use_api_server)
9363 // 2nd step: download server scores from score server to cache file
9364 // (as thread, as it might time out if the server is not reachable)
9365 ApiGetScoreAsThread(nr);
9369 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9371 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9373 // if score tape not uploaded, ask for uploading missing tapes later
9374 if (!setup.has_remaining_tapes)
9375 setup.ask_for_remaining_tapes = TRUE;
9377 setup.provide_uploading_tapes = TRUE;
9378 setup.has_remaining_tapes = TRUE;
9380 SaveSetup_ServerSetup();
9383 void SaveServerScore(int nr, boolean tape_saved)
9385 if (!runtime.use_api_server)
9387 PrepareScoreTapesForUpload(leveldir_current->subdir);
9392 ApiAddScoreAsThread(nr, tape_saved, NULL);
9395 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9396 char *score_tape_filename)
9398 if (!runtime.use_api_server)
9401 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9404 void LoadLocalAndServerScore(int nr, boolean download_score)
9406 int last_added_local = scores.last_added_local;
9407 boolean force_last_added = scores.force_last_added;
9409 // needed if only showing server scores
9410 setScoreInfoToDefaults();
9412 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9415 // restore last added local score entry (before merging server scores)
9416 scores.last_added = scores.last_added_local = last_added_local;
9418 if (setup.use_api_server &&
9419 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9421 // load server scores from cache file and trigger update from server
9422 LoadServerScore(nr, download_score);
9424 // merge local scores with scores from server
9428 if (force_last_added)
9429 scores.force_last_added = force_last_added;
9433 // ============================================================================
9434 // setup file functions
9435 // ============================================================================
9437 #define TOKEN_STR_PLAYER_PREFIX "player_"
9440 static struct TokenInfo global_setup_tokens[] =
9444 &setup.player_name, "player_name"
9448 &setup.multiple_users, "multiple_users"
9452 &setup.sound, "sound"
9456 &setup.sound_loops, "repeating_sound_loops"
9460 &setup.sound_music, "background_music"
9464 &setup.sound_simple, "simple_sound_effects"
9468 &setup.toons, "toons"
9472 &setup.scroll_delay, "scroll_delay"
9476 &setup.forced_scroll_delay, "forced_scroll_delay"
9480 &setup.scroll_delay_value, "scroll_delay_value"
9484 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9488 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9492 &setup.fade_screens, "fade_screens"
9496 &setup.autorecord, "automatic_tape_recording"
9500 &setup.autorecord_after_replay, "autorecord_after_replay"
9504 &setup.auto_pause_on_start, "auto_pause_on_start"
9508 &setup.show_titlescreen, "show_titlescreen"
9512 &setup.quick_doors, "quick_doors"
9516 &setup.team_mode, "team_mode"
9520 &setup.handicap, "handicap"
9524 &setup.skip_levels, "skip_levels"
9528 &setup.increment_levels, "increment_levels"
9532 &setup.auto_play_next_level, "auto_play_next_level"
9536 &setup.count_score_after_game, "count_score_after_game"
9540 &setup.show_scores_after_game, "show_scores_after_game"
9544 &setup.time_limit, "time_limit"
9548 &setup.fullscreen, "fullscreen"
9552 &setup.window_scaling_percent, "window_scaling_percent"
9556 &setup.window_scaling_quality, "window_scaling_quality"
9560 &setup.screen_rendering_mode, "screen_rendering_mode"
9564 &setup.vsync_mode, "vsync_mode"
9568 &setup.ask_on_escape, "ask_on_escape"
9572 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9576 &setup.ask_on_game_over, "ask_on_game_over"
9580 &setup.ask_on_quit_game, "ask_on_quit_game"
9584 &setup.ask_on_quit_program, "ask_on_quit_program"
9588 &setup.quick_switch, "quick_player_switch"
9592 &setup.input_on_focus, "input_on_focus"
9596 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9600 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9604 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9608 &setup.game_speed_extended, "game_speed_extended"
9612 &setup.game_frame_delay, "game_frame_delay"
9616 &setup.sp_show_border_elements, "sp_show_border_elements"
9620 &setup.small_game_graphics, "small_game_graphics"
9624 &setup.show_load_save_buttons, "show_load_save_buttons"
9628 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9632 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9636 &setup.graphics_set, "graphics_set"
9640 &setup.sounds_set, "sounds_set"
9644 &setup.music_set, "music_set"
9648 &setup.override_level_graphics, "override_level_graphics"
9652 &setup.override_level_sounds, "override_level_sounds"
9656 &setup.override_level_music, "override_level_music"
9660 &setup.volume_simple, "volume_simple"
9664 &setup.volume_loops, "volume_loops"
9668 &setup.volume_music, "volume_music"
9672 &setup.network_mode, "network_mode"
9676 &setup.network_player_nr, "network_player"
9680 &setup.network_server_hostname, "network_server_hostname"
9684 &setup.touch.control_type, "touch.control_type"
9688 &setup.touch.move_distance, "touch.move_distance"
9692 &setup.touch.drop_distance, "touch.drop_distance"
9696 &setup.touch.transparency, "touch.transparency"
9700 &setup.touch.draw_outlined, "touch.draw_outlined"
9704 &setup.touch.draw_pressed, "touch.draw_pressed"
9708 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9712 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9716 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9720 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9724 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9728 static struct TokenInfo auto_setup_tokens[] =
9732 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9736 static struct TokenInfo server_setup_tokens[] =
9740 &setup.player_uuid, "player_uuid"
9744 &setup.player_version, "player_version"
9748 &setup.use_api_server, TEST_PREFIX "use_api_server"
9752 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9756 &setup.api_server_password, TEST_PREFIX "api_server_password"
9760 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9764 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9768 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9772 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9776 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9780 static struct TokenInfo editor_setup_tokens[] =
9784 &setup.editor.el_classic, "editor.el_classic"
9788 &setup.editor.el_custom, "editor.el_custom"
9792 &setup.editor.el_user_defined, "editor.el_user_defined"
9796 &setup.editor.el_dynamic, "editor.el_dynamic"
9800 &setup.editor.el_headlines, "editor.el_headlines"
9804 &setup.editor.show_element_token, "editor.show_element_token"
9808 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9812 static struct TokenInfo editor_cascade_setup_tokens[] =
9816 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9820 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9824 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9828 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9832 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9836 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9840 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9844 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9848 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9852 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9856 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9860 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9864 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9868 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9872 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9876 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9880 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9884 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9888 static struct TokenInfo shortcut_setup_tokens[] =
9892 &setup.shortcut.save_game, "shortcut.save_game"
9896 &setup.shortcut.load_game, "shortcut.load_game"
9900 &setup.shortcut.restart_game, "shortcut.restart_game"
9904 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9908 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9912 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9916 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9920 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9924 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9928 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9932 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9936 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9940 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9944 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9948 &setup.shortcut.tape_record, "shortcut.tape_record"
9952 &setup.shortcut.tape_play, "shortcut.tape_play"
9956 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9960 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9964 &setup.shortcut.sound_music, "shortcut.sound_music"
9968 &setup.shortcut.snap_left, "shortcut.snap_left"
9972 &setup.shortcut.snap_right, "shortcut.snap_right"
9976 &setup.shortcut.snap_up, "shortcut.snap_up"
9980 &setup.shortcut.snap_down, "shortcut.snap_down"
9984 static struct SetupInputInfo setup_input;
9985 static struct TokenInfo player_setup_tokens[] =
9989 &setup_input.use_joystick, ".use_joystick"
9993 &setup_input.joy.device_name, ".joy.device_name"
9997 &setup_input.joy.xleft, ".joy.xleft"
10001 &setup_input.joy.xmiddle, ".joy.xmiddle"
10005 &setup_input.joy.xright, ".joy.xright"
10009 &setup_input.joy.yupper, ".joy.yupper"
10013 &setup_input.joy.ymiddle, ".joy.ymiddle"
10017 &setup_input.joy.ylower, ".joy.ylower"
10021 &setup_input.joy.snap, ".joy.snap_field"
10025 &setup_input.joy.drop, ".joy.place_bomb"
10029 &setup_input.key.left, ".key.move_left"
10033 &setup_input.key.right, ".key.move_right"
10037 &setup_input.key.up, ".key.move_up"
10041 &setup_input.key.down, ".key.move_down"
10045 &setup_input.key.snap, ".key.snap_field"
10049 &setup_input.key.drop, ".key.place_bomb"
10053 static struct TokenInfo system_setup_tokens[] =
10057 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10061 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10065 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10069 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10073 static struct TokenInfo internal_setup_tokens[] =
10077 &setup.internal.program_title, "program_title"
10081 &setup.internal.program_version, "program_version"
10085 &setup.internal.program_author, "program_author"
10089 &setup.internal.program_email, "program_email"
10093 &setup.internal.program_website, "program_website"
10097 &setup.internal.program_copyright, "program_copyright"
10101 &setup.internal.program_company, "program_company"
10105 &setup.internal.program_icon_file, "program_icon_file"
10109 &setup.internal.default_graphics_set, "default_graphics_set"
10113 &setup.internal.default_sounds_set, "default_sounds_set"
10117 &setup.internal.default_music_set, "default_music_set"
10121 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10125 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10129 &setup.internal.fallback_music_file, "fallback_music_file"
10133 &setup.internal.default_level_series, "default_level_series"
10137 &setup.internal.default_window_width, "default_window_width"
10141 &setup.internal.default_window_height, "default_window_height"
10145 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10149 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10153 &setup.internal.create_user_levelset, "create_user_levelset"
10157 &setup.internal.info_screens_from_main, "info_screens_from_main"
10161 &setup.internal.menu_game, "menu_game"
10165 &setup.internal.menu_engines, "menu_engines"
10169 &setup.internal.menu_editor, "menu_editor"
10173 &setup.internal.menu_graphics, "menu_graphics"
10177 &setup.internal.menu_sound, "menu_sound"
10181 &setup.internal.menu_artwork, "menu_artwork"
10185 &setup.internal.menu_input, "menu_input"
10189 &setup.internal.menu_touch, "menu_touch"
10193 &setup.internal.menu_shortcuts, "menu_shortcuts"
10197 &setup.internal.menu_exit, "menu_exit"
10201 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10205 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10209 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10213 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10217 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10221 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10225 &setup.internal.info_title, "info_title"
10229 &setup.internal.info_elements, "info_elements"
10233 &setup.internal.info_music, "info_music"
10237 &setup.internal.info_credits, "info_credits"
10241 &setup.internal.info_program, "info_program"
10245 &setup.internal.info_version, "info_version"
10249 &setup.internal.info_levelset, "info_levelset"
10253 &setup.internal.info_exit, "info_exit"
10257 static struct TokenInfo debug_setup_tokens[] =
10261 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10265 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10269 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10273 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10277 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10281 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10285 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10289 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10293 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10297 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10301 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10305 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10309 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10313 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10317 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10321 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10325 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10329 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10333 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10337 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10341 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10344 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10348 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10352 &setup.debug.xsn_mode, "debug.xsn_mode"
10356 &setup.debug.xsn_percent, "debug.xsn_percent"
10360 static struct TokenInfo options_setup_tokens[] =
10364 &setup.options.verbose, "options.verbose"
10368 static void setSetupInfoToDefaults(struct SetupInfo *si)
10372 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10374 si->multiple_users = TRUE;
10377 si->sound_loops = TRUE;
10378 si->sound_music = TRUE;
10379 si->sound_simple = TRUE;
10381 si->scroll_delay = TRUE;
10382 si->forced_scroll_delay = FALSE;
10383 si->scroll_delay_value = STD_SCROLL_DELAY;
10384 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10385 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10386 si->fade_screens = TRUE;
10387 si->autorecord = TRUE;
10388 si->autorecord_after_replay = TRUE;
10389 si->auto_pause_on_start = FALSE;
10390 si->show_titlescreen = TRUE;
10391 si->quick_doors = FALSE;
10392 si->team_mode = FALSE;
10393 si->handicap = TRUE;
10394 si->skip_levels = TRUE;
10395 si->increment_levels = TRUE;
10396 si->auto_play_next_level = TRUE;
10397 si->count_score_after_game = TRUE;
10398 si->show_scores_after_game = TRUE;
10399 si->time_limit = TRUE;
10400 si->fullscreen = FALSE;
10401 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10402 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10403 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10404 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10405 si->ask_on_escape = TRUE;
10406 si->ask_on_escape_editor = TRUE;
10407 si->ask_on_game_over = TRUE;
10408 si->ask_on_quit_game = TRUE;
10409 si->ask_on_quit_program = TRUE;
10410 si->quick_switch = FALSE;
10411 si->input_on_focus = FALSE;
10412 si->prefer_aga_graphics = TRUE;
10413 si->prefer_lowpass_sounds = FALSE;
10414 si->prefer_extra_panel_items = TRUE;
10415 si->game_speed_extended = FALSE;
10416 si->game_frame_delay = GAME_FRAME_DELAY;
10417 si->sp_show_border_elements = FALSE;
10418 si->small_game_graphics = FALSE;
10419 si->show_load_save_buttons = FALSE;
10420 si->show_undo_redo_buttons = FALSE;
10421 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10423 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10424 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10425 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10427 si->override_level_graphics = FALSE;
10428 si->override_level_sounds = FALSE;
10429 si->override_level_music = FALSE;
10431 si->volume_simple = 100; // percent
10432 si->volume_loops = 100; // percent
10433 si->volume_music = 100; // percent
10435 si->network_mode = FALSE;
10436 si->network_player_nr = 0; // first player
10437 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10439 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10440 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10441 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10442 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10443 si->touch.draw_outlined = TRUE;
10444 si->touch.draw_pressed = TRUE;
10446 for (i = 0; i < 2; i++)
10448 char *default_grid_button[6][2] =
10454 { "111222", " vv " },
10455 { "111222", " vv " }
10457 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10458 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10459 int min_xsize = MIN(6, grid_xsize);
10460 int min_ysize = MIN(6, grid_ysize);
10461 int startx = grid_xsize - min_xsize;
10462 int starty = grid_ysize - min_ysize;
10465 // virtual buttons grid can only be set to defaults if video is initialized
10466 // (this will be repeated if virtual buttons are not loaded from setup file)
10467 if (video.initialized)
10469 si->touch.grid_xsize[i] = grid_xsize;
10470 si->touch.grid_ysize[i] = grid_ysize;
10474 si->touch.grid_xsize[i] = -1;
10475 si->touch.grid_ysize[i] = -1;
10478 for (x = 0; x < MAX_GRID_XSIZE; x++)
10479 for (y = 0; y < MAX_GRID_YSIZE; y++)
10480 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10482 for (x = 0; x < min_xsize; x++)
10483 for (y = 0; y < min_ysize; y++)
10484 si->touch.grid_button[i][x][starty + y] =
10485 default_grid_button[y][0][x];
10487 for (x = 0; x < min_xsize; x++)
10488 for (y = 0; y < min_ysize; y++)
10489 si->touch.grid_button[i][startx + x][starty + y] =
10490 default_grid_button[y][1][x];
10493 si->touch.grid_initialized = video.initialized;
10495 si->touch.overlay_buttons = FALSE;
10497 si->editor.el_boulderdash = TRUE;
10498 si->editor.el_emerald_mine = TRUE;
10499 si->editor.el_emerald_mine_club = TRUE;
10500 si->editor.el_more = TRUE;
10501 si->editor.el_sokoban = TRUE;
10502 si->editor.el_supaplex = TRUE;
10503 si->editor.el_diamond_caves = TRUE;
10504 si->editor.el_dx_boulderdash = TRUE;
10506 si->editor.el_mirror_magic = TRUE;
10507 si->editor.el_deflektor = TRUE;
10509 si->editor.el_chars = TRUE;
10510 si->editor.el_steel_chars = TRUE;
10512 si->editor.el_classic = TRUE;
10513 si->editor.el_custom = TRUE;
10515 si->editor.el_user_defined = FALSE;
10516 si->editor.el_dynamic = TRUE;
10518 si->editor.el_headlines = TRUE;
10520 si->editor.show_element_token = FALSE;
10522 si->editor.show_read_only_warning = TRUE;
10524 si->editor.use_template_for_new_levels = TRUE;
10526 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10527 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10528 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10529 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10530 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10532 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10533 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10534 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10535 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10536 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10538 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10539 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10540 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10541 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10542 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10543 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10545 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10546 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10547 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10549 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10550 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10551 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10552 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10554 for (i = 0; i < MAX_PLAYERS; i++)
10556 si->input[i].use_joystick = FALSE;
10557 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10558 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10559 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10560 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10561 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10562 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10563 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10564 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10565 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10566 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10567 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10568 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10569 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10570 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10571 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10574 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10575 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10576 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10577 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10579 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10580 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10581 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10582 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10583 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10584 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10585 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10587 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10589 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10590 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10591 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10593 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10594 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10595 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10597 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10598 si->internal.choose_from_top_leveldir = FALSE;
10599 si->internal.show_scaling_in_title = TRUE;
10600 si->internal.create_user_levelset = TRUE;
10601 si->internal.info_screens_from_main = FALSE;
10603 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10604 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10606 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10607 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10608 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10609 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10610 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10611 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10612 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10613 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10614 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10615 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10617 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10618 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10619 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10620 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10621 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10622 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10623 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10624 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10625 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10626 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10628 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10629 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10631 si->debug.show_frames_per_second = FALSE;
10633 si->debug.xsn_mode = AUTO;
10634 si->debug.xsn_percent = 0;
10636 si->options.verbose = FALSE;
10638 #if defined(PLATFORM_ANDROID)
10639 si->fullscreen = TRUE;
10640 si->touch.overlay_buttons = TRUE;
10643 setHideSetupEntry(&setup.debug.xsn_mode);
10646 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10648 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10651 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10653 si->player_uuid = NULL; // (will be set later)
10654 si->player_version = 1; // (will be set later)
10656 si->use_api_server = TRUE;
10657 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10658 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10659 si->ask_for_uploading_tapes = TRUE;
10660 si->ask_for_remaining_tapes = FALSE;
10661 si->provide_uploading_tapes = TRUE;
10662 si->ask_for_using_api_server = TRUE;
10663 si->has_remaining_tapes = FALSE;
10666 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10668 si->editor_cascade.el_bd = TRUE;
10669 si->editor_cascade.el_em = TRUE;
10670 si->editor_cascade.el_emc = TRUE;
10671 si->editor_cascade.el_rnd = TRUE;
10672 si->editor_cascade.el_sb = TRUE;
10673 si->editor_cascade.el_sp = TRUE;
10674 si->editor_cascade.el_dc = TRUE;
10675 si->editor_cascade.el_dx = TRUE;
10677 si->editor_cascade.el_mm = TRUE;
10678 si->editor_cascade.el_df = TRUE;
10680 si->editor_cascade.el_chars = FALSE;
10681 si->editor_cascade.el_steel_chars = FALSE;
10682 si->editor_cascade.el_ce = FALSE;
10683 si->editor_cascade.el_ge = FALSE;
10684 si->editor_cascade.el_es = FALSE;
10685 si->editor_cascade.el_ref = FALSE;
10686 si->editor_cascade.el_user = FALSE;
10687 si->editor_cascade.el_dynamic = FALSE;
10690 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10692 static char *getHideSetupToken(void *setup_value)
10694 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10696 if (setup_value != NULL)
10697 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10699 return hide_setup_token;
10702 void setHideSetupEntry(void *setup_value)
10704 char *hide_setup_token = getHideSetupToken(setup_value);
10706 if (hide_setup_hash == NULL)
10707 hide_setup_hash = newSetupFileHash();
10709 if (setup_value != NULL)
10710 setHashEntry(hide_setup_hash, hide_setup_token, "");
10713 void removeHideSetupEntry(void *setup_value)
10715 char *hide_setup_token = getHideSetupToken(setup_value);
10717 if (setup_value != NULL)
10718 removeHashEntry(hide_setup_hash, hide_setup_token);
10721 boolean hideSetupEntry(void *setup_value)
10723 char *hide_setup_token = getHideSetupToken(setup_value);
10725 return (setup_value != NULL &&
10726 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10729 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10730 struct TokenInfo *token_info,
10731 int token_nr, char *token_text)
10733 char *token_hide_text = getStringCat2(token_text, ".hide");
10734 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10736 // set the value of this setup option in the setup option structure
10737 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10739 // check if this setup option should be hidden in the setup menu
10740 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10741 setHideSetupEntry(token_info[token_nr].value);
10743 free(token_hide_text);
10746 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10747 struct TokenInfo *token_info,
10750 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10751 token_info[token_nr].text);
10754 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10758 if (!setup_file_hash)
10761 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10762 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10764 setup.touch.grid_initialized = TRUE;
10765 for (i = 0; i < 2; i++)
10767 int grid_xsize = setup.touch.grid_xsize[i];
10768 int grid_ysize = setup.touch.grid_ysize[i];
10771 // if virtual buttons are not loaded from setup file, repeat initializing
10772 // virtual buttons grid with default values later when video is initialized
10773 if (grid_xsize == -1 ||
10776 setup.touch.grid_initialized = FALSE;
10781 for (y = 0; y < grid_ysize; y++)
10783 char token_string[MAX_LINE_LEN];
10785 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10787 char *value_string = getHashEntry(setup_file_hash, token_string);
10789 if (value_string == NULL)
10792 for (x = 0; x < grid_xsize; x++)
10794 char c = value_string[x];
10796 setup.touch.grid_button[i][x][y] =
10797 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10802 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10803 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10805 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10806 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10808 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10812 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10814 setup_input = setup.input[pnr];
10815 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10817 char full_token[100];
10819 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10820 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10823 setup.input[pnr] = setup_input;
10826 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10827 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10829 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10830 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10832 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10833 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10835 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10836 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10838 setHideRelatedSetupEntries();
10841 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10845 if (!setup_file_hash)
10848 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10849 setSetupInfo(auto_setup_tokens, i,
10850 getHashEntry(setup_file_hash,
10851 auto_setup_tokens[i].text));
10854 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10858 if (!setup_file_hash)
10861 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10862 setSetupInfo(server_setup_tokens, i,
10863 getHashEntry(setup_file_hash,
10864 server_setup_tokens[i].text));
10867 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10871 if (!setup_file_hash)
10874 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10875 setSetupInfo(editor_cascade_setup_tokens, i,
10876 getHashEntry(setup_file_hash,
10877 editor_cascade_setup_tokens[i].text));
10880 void LoadUserNames(void)
10882 int last_user_nr = user.nr;
10885 if (global.user_names != NULL)
10887 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10888 checked_free(global.user_names[i]);
10890 checked_free(global.user_names);
10893 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10895 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10899 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10901 if (setup_file_hash)
10903 char *player_name = getHashEntry(setup_file_hash, "player_name");
10905 global.user_names[i] = getFixedUserName(player_name);
10907 freeSetupFileHash(setup_file_hash);
10910 if (global.user_names[i] == NULL)
10911 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10914 user.nr = last_user_nr;
10917 void LoadSetupFromFilename(char *filename)
10919 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10921 if (setup_file_hash)
10923 decodeSetupFileHash_Default(setup_file_hash);
10925 freeSetupFileHash(setup_file_hash);
10929 Debug("setup", "using default setup values");
10933 static void LoadSetup_SpecialPostProcessing(void)
10935 char *player_name_new;
10937 // needed to work around problems with fixed length strings
10938 player_name_new = getFixedUserName(setup.player_name);
10939 free(setup.player_name);
10940 setup.player_name = player_name_new;
10942 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10943 if (setup.scroll_delay == FALSE)
10945 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10946 setup.scroll_delay = TRUE; // now always "on"
10949 // make sure that scroll delay value stays inside valid range
10950 setup.scroll_delay_value =
10951 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10954 void LoadSetup_Default(void)
10958 // always start with reliable default values
10959 setSetupInfoToDefaults(&setup);
10961 // try to load setup values from default setup file
10962 filename = getDefaultSetupFilename();
10964 if (fileExists(filename))
10965 LoadSetupFromFilename(filename);
10967 // try to load setup values from platform setup file
10968 filename = getPlatformSetupFilename();
10970 if (fileExists(filename))
10971 LoadSetupFromFilename(filename);
10973 // try to load setup values from user setup file
10974 filename = getSetupFilename();
10976 LoadSetupFromFilename(filename);
10978 LoadSetup_SpecialPostProcessing();
10981 void LoadSetup_AutoSetup(void)
10983 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10984 SetupFileHash *setup_file_hash = NULL;
10986 // always start with reliable default values
10987 setSetupInfoToDefaults_AutoSetup(&setup);
10989 setup_file_hash = loadSetupFileHash(filename);
10991 if (setup_file_hash)
10993 decodeSetupFileHash_AutoSetup(setup_file_hash);
10995 freeSetupFileHash(setup_file_hash);
11001 void LoadSetup_ServerSetup(void)
11003 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11004 SetupFileHash *setup_file_hash = NULL;
11006 // always start with reliable default values
11007 setSetupInfoToDefaults_ServerSetup(&setup);
11009 setup_file_hash = loadSetupFileHash(filename);
11011 if (setup_file_hash)
11013 decodeSetupFileHash_ServerSetup(setup_file_hash);
11015 freeSetupFileHash(setup_file_hash);
11020 if (setup.player_uuid == NULL)
11022 // player UUID does not yet exist in setup file
11023 setup.player_uuid = getStringCopy(getUUID());
11024 setup.player_version = 2;
11026 SaveSetup_ServerSetup();
11030 void LoadSetup_EditorCascade(void)
11032 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11033 SetupFileHash *setup_file_hash = NULL;
11035 // always start with reliable default values
11036 setSetupInfoToDefaults_EditorCascade(&setup);
11038 setup_file_hash = loadSetupFileHash(filename);
11040 if (setup_file_hash)
11042 decodeSetupFileHash_EditorCascade(setup_file_hash);
11044 freeSetupFileHash(setup_file_hash);
11050 void LoadSetup(void)
11052 LoadSetup_Default();
11053 LoadSetup_AutoSetup();
11054 LoadSetup_ServerSetup();
11055 LoadSetup_EditorCascade();
11058 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11059 char *mapping_line)
11061 char mapping_guid[MAX_LINE_LEN];
11062 char *mapping_start, *mapping_end;
11064 // get GUID from game controller mapping line: copy complete line
11065 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11066 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11068 // get GUID from game controller mapping line: cut after GUID part
11069 mapping_start = strchr(mapping_guid, ',');
11070 if (mapping_start != NULL)
11071 *mapping_start = '\0';
11073 // cut newline from game controller mapping line
11074 mapping_end = strchr(mapping_line, '\n');
11075 if (mapping_end != NULL)
11076 *mapping_end = '\0';
11078 // add mapping entry to game controller mappings hash
11079 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11082 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11087 if (!(file = fopen(filename, MODE_READ)))
11089 Warn("cannot read game controller mappings file '%s'", filename);
11094 while (!feof(file))
11096 char line[MAX_LINE_LEN];
11098 if (!fgets(line, MAX_LINE_LEN, file))
11101 addGameControllerMappingToHash(mappings_hash, line);
11107 void SaveSetup_Default(void)
11109 char *filename = getSetupFilename();
11113 InitUserDataDirectory();
11115 if (!(file = fopen(filename, MODE_WRITE)))
11117 Warn("cannot write setup file '%s'", filename);
11122 fprintFileHeader(file, SETUP_FILENAME);
11124 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11126 // just to make things nicer :)
11127 if (global_setup_tokens[i].value == &setup.multiple_users ||
11128 global_setup_tokens[i].value == &setup.sound ||
11129 global_setup_tokens[i].value == &setup.graphics_set ||
11130 global_setup_tokens[i].value == &setup.volume_simple ||
11131 global_setup_tokens[i].value == &setup.network_mode ||
11132 global_setup_tokens[i].value == &setup.touch.control_type ||
11133 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11134 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11135 fprintf(file, "\n");
11137 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11140 for (i = 0; i < 2; i++)
11142 int grid_xsize = setup.touch.grid_xsize[i];
11143 int grid_ysize = setup.touch.grid_ysize[i];
11146 fprintf(file, "\n");
11148 for (y = 0; y < grid_ysize; y++)
11150 char token_string[MAX_LINE_LEN];
11151 char value_string[MAX_LINE_LEN];
11153 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11155 for (x = 0; x < grid_xsize; x++)
11157 char c = setup.touch.grid_button[i][x][y];
11159 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11162 value_string[grid_xsize] = '\0';
11164 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11168 fprintf(file, "\n");
11169 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11170 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11172 fprintf(file, "\n");
11173 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11174 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11176 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11180 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11181 fprintf(file, "\n");
11183 setup_input = setup.input[pnr];
11184 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11185 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11188 fprintf(file, "\n");
11189 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11190 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11192 // (internal setup values not saved to user setup file)
11194 fprintf(file, "\n");
11195 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11196 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11197 setup.debug.xsn_mode != AUTO)
11198 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11200 fprintf(file, "\n");
11201 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11202 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11206 SetFilePermissions(filename, PERMS_PRIVATE);
11209 void SaveSetup_AutoSetup(void)
11211 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11215 InitUserDataDirectory();
11217 if (!(file = fopen(filename, MODE_WRITE)))
11219 Warn("cannot write auto setup file '%s'", filename);
11226 fprintFileHeader(file, AUTOSETUP_FILENAME);
11228 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11229 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11233 SetFilePermissions(filename, PERMS_PRIVATE);
11238 void SaveSetup_ServerSetup(void)
11240 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11244 InitUserDataDirectory();
11246 if (!(file = fopen(filename, MODE_WRITE)))
11248 Warn("cannot write server setup file '%s'", filename);
11255 fprintFileHeader(file, SERVERSETUP_FILENAME);
11257 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11259 // just to make things nicer :)
11260 if (server_setup_tokens[i].value == &setup.use_api_server)
11261 fprintf(file, "\n");
11263 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11268 SetFilePermissions(filename, PERMS_PRIVATE);
11273 void SaveSetup_EditorCascade(void)
11275 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11279 InitUserDataDirectory();
11281 if (!(file = fopen(filename, MODE_WRITE)))
11283 Warn("cannot write editor cascade state file '%s'", filename);
11290 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11292 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11293 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11297 SetFilePermissions(filename, PERMS_PRIVATE);
11302 void SaveSetup(void)
11304 SaveSetup_Default();
11305 SaveSetup_AutoSetup();
11306 SaveSetup_ServerSetup();
11307 SaveSetup_EditorCascade();
11310 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11315 if (!(file = fopen(filename, MODE_WRITE)))
11317 Warn("cannot write game controller mappings file '%s'", filename);
11322 BEGIN_HASH_ITERATION(mappings_hash, itr)
11324 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11326 END_HASH_ITERATION(mappings_hash, itr)
11331 void SaveSetup_AddGameControllerMapping(char *mapping)
11333 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11334 SetupFileHash *mappings_hash = newSetupFileHash();
11336 InitUserDataDirectory();
11338 // load existing personal game controller mappings
11339 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11341 // add new mapping to personal game controller mappings
11342 addGameControllerMappingToHash(mappings_hash, mapping);
11344 // save updated personal game controller mappings
11345 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11347 freeSetupFileHash(mappings_hash);
11351 void LoadCustomElementDescriptions(void)
11353 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11354 SetupFileHash *setup_file_hash;
11357 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11359 if (element_info[i].custom_description != NULL)
11361 free(element_info[i].custom_description);
11362 element_info[i].custom_description = NULL;
11366 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11369 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11371 char *token = getStringCat2(element_info[i].token_name, ".name");
11372 char *value = getHashEntry(setup_file_hash, token);
11375 element_info[i].custom_description = getStringCopy(value);
11380 freeSetupFileHash(setup_file_hash);
11383 static int getElementFromToken(char *token)
11385 char *value = getHashEntry(element_token_hash, token);
11388 return atoi(value);
11390 Warn("unknown element token '%s'", token);
11392 return EL_UNDEFINED;
11395 void FreeGlobalAnimEventInfo(void)
11397 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11399 if (gaei->event_list == NULL)
11404 for (i = 0; i < gaei->num_event_lists; i++)
11406 checked_free(gaei->event_list[i]->event_value);
11407 checked_free(gaei->event_list[i]);
11410 checked_free(gaei->event_list);
11412 gaei->event_list = NULL;
11413 gaei->num_event_lists = 0;
11416 static int AddGlobalAnimEventList(void)
11418 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11419 int list_pos = gaei->num_event_lists++;
11421 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11422 sizeof(struct GlobalAnimEventListInfo *));
11424 gaei->event_list[list_pos] =
11425 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11427 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11429 gaeli->event_value = NULL;
11430 gaeli->num_event_values = 0;
11435 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11437 // do not add empty global animation events
11438 if (event_value == ANIM_EVENT_NONE)
11441 // if list position is undefined, create new list
11442 if (list_pos == ANIM_EVENT_UNDEFINED)
11443 list_pos = AddGlobalAnimEventList();
11445 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11446 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11447 int value_pos = gaeli->num_event_values++;
11449 gaeli->event_value = checked_realloc(gaeli->event_value,
11450 gaeli->num_event_values * sizeof(int *));
11452 gaeli->event_value[value_pos] = event_value;
11457 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11459 if (list_pos == ANIM_EVENT_UNDEFINED)
11460 return ANIM_EVENT_NONE;
11462 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11463 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11465 return gaeli->event_value[value_pos];
11468 int GetGlobalAnimEventValueCount(int list_pos)
11470 if (list_pos == ANIM_EVENT_UNDEFINED)
11473 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11474 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11476 return gaeli->num_event_values;
11479 // This function checks if a string <s> of the format "string1, string2, ..."
11480 // exactly contains a string <s_contained>.
11482 static boolean string_has_parameter(char *s, char *s_contained)
11486 if (s == NULL || s_contained == NULL)
11489 if (strlen(s_contained) > strlen(s))
11492 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11494 char next_char = s[strlen(s_contained)];
11496 // check if next character is delimiter or whitespace
11497 return (next_char == ',' || next_char == '\0' ||
11498 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11501 // check if string contains another parameter string after a comma
11502 substring = strchr(s, ',');
11503 if (substring == NULL) // string does not contain a comma
11506 // advance string pointer to next character after the comma
11509 // skip potential whitespaces after the comma
11510 while (*substring == ' ' || *substring == '\t')
11513 return string_has_parameter(substring, s_contained);
11516 static int get_anim_parameter_value(char *s)
11518 int event_value[] =
11526 char *pattern_1[] =
11534 char *pattern_2 = ".part_";
11535 char *matching_char = NULL;
11537 int pattern_1_len = 0;
11538 int result = ANIM_EVENT_NONE;
11541 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11543 matching_char = strstr(s_ptr, pattern_1[i]);
11544 pattern_1_len = strlen(pattern_1[i]);
11545 result = event_value[i];
11547 if (matching_char != NULL)
11551 if (matching_char == NULL)
11552 return ANIM_EVENT_NONE;
11554 s_ptr = matching_char + pattern_1_len;
11556 // check for main animation number ("anim_X" or "anim_XX")
11557 if (*s_ptr >= '0' && *s_ptr <= '9')
11559 int gic_anim_nr = (*s_ptr++ - '0');
11561 if (*s_ptr >= '0' && *s_ptr <= '9')
11562 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11564 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11565 return ANIM_EVENT_NONE;
11567 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11571 // invalid main animation number specified
11573 return ANIM_EVENT_NONE;
11576 // check for animation part number ("part_X" or "part_XX") (optional)
11577 if (strPrefix(s_ptr, pattern_2))
11579 s_ptr += strlen(pattern_2);
11581 if (*s_ptr >= '0' && *s_ptr <= '9')
11583 int gic_part_nr = (*s_ptr++ - '0');
11585 if (*s_ptr >= '0' && *s_ptr <= '9')
11586 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11588 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11589 return ANIM_EVENT_NONE;
11591 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11595 // invalid animation part number specified
11597 return ANIM_EVENT_NONE;
11601 // discard result if next character is neither delimiter nor whitespace
11602 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11603 *s_ptr == ' ' || *s_ptr == '\t'))
11604 return ANIM_EVENT_NONE;
11609 static int get_anim_parameter_values(char *s)
11611 int list_pos = ANIM_EVENT_UNDEFINED;
11612 int event_value = ANIM_EVENT_DEFAULT;
11614 if (string_has_parameter(s, "any"))
11615 event_value |= ANIM_EVENT_ANY;
11617 if (string_has_parameter(s, "click:self") ||
11618 string_has_parameter(s, "click") ||
11619 string_has_parameter(s, "self"))
11620 event_value |= ANIM_EVENT_SELF;
11622 if (string_has_parameter(s, "unclick:any"))
11623 event_value |= ANIM_EVENT_UNCLICK_ANY;
11625 // if animation event found, add it to global animation event list
11626 if (event_value != ANIM_EVENT_NONE)
11627 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11631 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11632 event_value = get_anim_parameter_value(s);
11634 // if animation event found, add it to global animation event list
11635 if (event_value != ANIM_EVENT_NONE)
11636 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11638 // continue with next part of the string, starting with next comma
11639 s = strchr(s + 1, ',');
11645 static int get_anim_action_parameter_value(char *token)
11647 // check most common default case first to massively speed things up
11648 if (strEqual(token, ARG_UNDEFINED))
11649 return ANIM_EVENT_ACTION_NONE;
11651 int result = getImageIDFromToken(token);
11655 char *gfx_token = getStringCat2("gfx.", token);
11657 result = getImageIDFromToken(gfx_token);
11659 checked_free(gfx_token);
11664 Key key = getKeyFromX11KeyName(token);
11666 if (key != KSYM_UNDEFINED)
11667 result = -(int)key;
11674 result = get_hash_from_key(token); // unsigned int => int
11675 result = ABS(result); // may be negative now
11676 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11678 setHashEntry(anim_url_hash, int2str(result, 0), token);
11683 result = ANIM_EVENT_ACTION_NONE;
11688 int get_parameter_value(char *value_raw, char *suffix, int type)
11690 char *value = getStringToLower(value_raw);
11691 int result = 0; // probably a save default value
11693 if (strEqual(suffix, ".direction"))
11695 result = (strEqual(value, "left") ? MV_LEFT :
11696 strEqual(value, "right") ? MV_RIGHT :
11697 strEqual(value, "up") ? MV_UP :
11698 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11700 else if (strEqual(suffix, ".position"))
11702 result = (strEqual(value, "left") ? POS_LEFT :
11703 strEqual(value, "right") ? POS_RIGHT :
11704 strEqual(value, "top") ? POS_TOP :
11705 strEqual(value, "upper") ? POS_UPPER :
11706 strEqual(value, "middle") ? POS_MIDDLE :
11707 strEqual(value, "lower") ? POS_LOWER :
11708 strEqual(value, "bottom") ? POS_BOTTOM :
11709 strEqual(value, "any") ? POS_ANY :
11710 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11712 else if (strEqual(suffix, ".align"))
11714 result = (strEqual(value, "left") ? ALIGN_LEFT :
11715 strEqual(value, "right") ? ALIGN_RIGHT :
11716 strEqual(value, "center") ? ALIGN_CENTER :
11717 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11719 else if (strEqual(suffix, ".valign"))
11721 result = (strEqual(value, "top") ? VALIGN_TOP :
11722 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11723 strEqual(value, "middle") ? VALIGN_MIDDLE :
11724 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11726 else if (strEqual(suffix, ".anim_mode"))
11728 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11729 string_has_parameter(value, "loop") ? ANIM_LOOP :
11730 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11731 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11732 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11733 string_has_parameter(value, "random") ? ANIM_RANDOM :
11734 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11735 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11736 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11737 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11738 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11739 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11740 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11741 string_has_parameter(value, "all") ? ANIM_ALL :
11742 string_has_parameter(value, "tiled") ? ANIM_TILED :
11745 if (string_has_parameter(value, "once"))
11746 result |= ANIM_ONCE;
11748 if (string_has_parameter(value, "reverse"))
11749 result |= ANIM_REVERSE;
11751 if (string_has_parameter(value, "opaque_player"))
11752 result |= ANIM_OPAQUE_PLAYER;
11754 if (string_has_parameter(value, "static_panel"))
11755 result |= ANIM_STATIC_PANEL;
11757 else if (strEqual(suffix, ".init_event") ||
11758 strEqual(suffix, ".anim_event"))
11760 result = get_anim_parameter_values(value);
11762 else if (strEqual(suffix, ".init_delay_action") ||
11763 strEqual(suffix, ".anim_delay_action") ||
11764 strEqual(suffix, ".post_delay_action") ||
11765 strEqual(suffix, ".init_event_action") ||
11766 strEqual(suffix, ".anim_event_action"))
11768 result = get_anim_action_parameter_value(value_raw);
11770 else if (strEqual(suffix, ".class"))
11772 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11773 get_hash_from_key(value));
11775 else if (strEqual(suffix, ".style"))
11777 result = STYLE_DEFAULT;
11779 if (string_has_parameter(value, "accurate_borders"))
11780 result |= STYLE_ACCURATE_BORDERS;
11782 if (string_has_parameter(value, "inner_corners"))
11783 result |= STYLE_INNER_CORNERS;
11785 if (string_has_parameter(value, "reverse"))
11786 result |= STYLE_REVERSE;
11788 if (string_has_parameter(value, "leftmost_position"))
11789 result |= STYLE_LEFTMOST_POSITION;
11791 if (string_has_parameter(value, "block_clicks"))
11792 result |= STYLE_BLOCK;
11794 if (string_has_parameter(value, "passthrough_clicks"))
11795 result |= STYLE_PASSTHROUGH;
11797 if (string_has_parameter(value, "multiple_actions"))
11798 result |= STYLE_MULTIPLE_ACTIONS;
11800 else if (strEqual(suffix, ".fade_mode"))
11802 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11803 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11804 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11805 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11806 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11807 FADE_MODE_DEFAULT);
11809 else if (strEqual(suffix, ".auto_delay_unit"))
11811 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11812 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11813 AUTO_DELAY_UNIT_DEFAULT);
11815 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11817 result = gfx.get_font_from_token_function(value);
11819 else // generic parameter of type integer or boolean
11821 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11822 type == TYPE_INTEGER ? get_integer_from_string(value) :
11823 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11824 ARG_UNDEFINED_VALUE);
11832 static int get_token_parameter_value(char *token, char *value_raw)
11836 if (token == NULL || value_raw == NULL)
11837 return ARG_UNDEFINED_VALUE;
11839 suffix = strrchr(token, '.');
11840 if (suffix == NULL)
11843 if (strEqual(suffix, ".element"))
11844 return getElementFromToken(value_raw);
11846 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11847 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11850 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11851 boolean ignore_defaults)
11855 for (i = 0; image_config_vars[i].token != NULL; i++)
11857 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11859 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11860 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11864 *image_config_vars[i].value =
11865 get_token_parameter_value(image_config_vars[i].token, value);
11869 void InitMenuDesignSettings_Static(void)
11871 // always start with reliable default values from static default config
11872 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11875 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11879 // the following initializes hierarchical values from static configuration
11881 // special case: initialize "ARG_DEFAULT" values in static default config
11882 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11883 titlescreen_initial_first_default.fade_mode =
11884 title_initial_first_default.fade_mode;
11885 titlescreen_initial_first_default.fade_delay =
11886 title_initial_first_default.fade_delay;
11887 titlescreen_initial_first_default.post_delay =
11888 title_initial_first_default.post_delay;
11889 titlescreen_initial_first_default.auto_delay =
11890 title_initial_first_default.auto_delay;
11891 titlescreen_initial_first_default.auto_delay_unit =
11892 title_initial_first_default.auto_delay_unit;
11893 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
11894 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11895 titlescreen_first_default.post_delay = title_first_default.post_delay;
11896 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11897 titlescreen_first_default.auto_delay_unit =
11898 title_first_default.auto_delay_unit;
11899 titlemessage_initial_first_default.fade_mode =
11900 title_initial_first_default.fade_mode;
11901 titlemessage_initial_first_default.fade_delay =
11902 title_initial_first_default.fade_delay;
11903 titlemessage_initial_first_default.post_delay =
11904 title_initial_first_default.post_delay;
11905 titlemessage_initial_first_default.auto_delay =
11906 title_initial_first_default.auto_delay;
11907 titlemessage_initial_first_default.auto_delay_unit =
11908 title_initial_first_default.auto_delay_unit;
11909 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
11910 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11911 titlemessage_first_default.post_delay = title_first_default.post_delay;
11912 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11913 titlemessage_first_default.auto_delay_unit =
11914 title_first_default.auto_delay_unit;
11916 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
11917 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11918 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11919 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11920 titlescreen_initial_default.auto_delay_unit =
11921 title_initial_default.auto_delay_unit;
11922 titlescreen_default.fade_mode = title_default.fade_mode;
11923 titlescreen_default.fade_delay = title_default.fade_delay;
11924 titlescreen_default.post_delay = title_default.post_delay;
11925 titlescreen_default.auto_delay = title_default.auto_delay;
11926 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11927 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11928 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11929 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11930 titlemessage_initial_default.auto_delay_unit =
11931 title_initial_default.auto_delay_unit;
11932 titlemessage_default.fade_mode = title_default.fade_mode;
11933 titlemessage_default.fade_delay = title_default.fade_delay;
11934 titlemessage_default.post_delay = title_default.post_delay;
11935 titlemessage_default.auto_delay = title_default.auto_delay;
11936 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11938 // special case: initialize "ARG_DEFAULT" values in static default config
11939 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11940 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11942 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11943 titlescreen_first[i] = titlescreen_first_default;
11944 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11945 titlemessage_first[i] = titlemessage_first_default;
11947 titlescreen_initial[i] = titlescreen_initial_default;
11948 titlescreen[i] = titlescreen_default;
11949 titlemessage_initial[i] = titlemessage_initial_default;
11950 titlemessage[i] = titlemessage_default;
11953 // special case: initialize "ARG_DEFAULT" values in static default config
11954 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11955 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11957 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11960 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11961 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11962 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11965 // special case: initialize "ARG_DEFAULT" values in static default config
11966 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11967 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11969 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11970 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11971 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11973 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11976 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11980 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11984 struct XY *dst, *src;
11986 game_buttons_xy[] =
11988 { &game.button.save, &game.button.stop },
11989 { &game.button.pause2, &game.button.pause },
11990 { &game.button.load, &game.button.play },
11991 { &game.button.undo, &game.button.stop },
11992 { &game.button.redo, &game.button.play },
11998 // special case: initialize later added SETUP list size from LEVELS value
11999 if (menu.list_size[GAME_MODE_SETUP] == -1)
12000 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12002 // set default position for snapshot buttons to stop/pause/play buttons
12003 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12004 if ((*game_buttons_xy[i].dst).x == -1 &&
12005 (*game_buttons_xy[i].dst).y == -1)
12006 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12008 // --------------------------------------------------------------------------
12009 // dynamic viewports (including playfield margins, borders and alignments)
12010 // --------------------------------------------------------------------------
12012 // dynamic viewports currently only supported for landscape mode
12013 int display_width = MAX(video.display_width, video.display_height);
12014 int display_height = MIN(video.display_width, video.display_height);
12016 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12018 struct RectWithBorder *vp_window = &viewport.window[i];
12019 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12020 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12021 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12022 boolean dynamic_window_width = (vp_window->min_width != -1);
12023 boolean dynamic_window_height = (vp_window->min_height != -1);
12024 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12025 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12027 // adjust window size if min/max width/height is specified
12029 if (vp_window->min_width != -1)
12031 int window_width = display_width;
12033 // when using static window height, use aspect ratio of display
12034 if (vp_window->min_height == -1)
12035 window_width = vp_window->height * display_width / display_height;
12037 vp_window->width = MAX(vp_window->min_width, window_width);
12040 if (vp_window->min_height != -1)
12042 int window_height = display_height;
12044 // when using static window width, use aspect ratio of display
12045 if (vp_window->min_width == -1)
12046 window_height = vp_window->width * display_height / display_width;
12048 vp_window->height = MAX(vp_window->min_height, window_height);
12051 if (vp_window->max_width != -1)
12052 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12054 if (vp_window->max_height != -1)
12055 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12057 int playfield_width = vp_window->width;
12058 int playfield_height = vp_window->height;
12060 // adjust playfield size and position according to specified margins
12062 playfield_width -= vp_playfield->margin_left;
12063 playfield_width -= vp_playfield->margin_right;
12065 playfield_height -= vp_playfield->margin_top;
12066 playfield_height -= vp_playfield->margin_bottom;
12068 // adjust playfield size if min/max width/height is specified
12070 if (vp_playfield->min_width != -1)
12071 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12073 if (vp_playfield->min_height != -1)
12074 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12076 if (vp_playfield->max_width != -1)
12077 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12079 if (vp_playfield->max_height != -1)
12080 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12082 // adjust playfield position according to specified alignment
12084 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12085 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12086 else if (vp_playfield->align == ALIGN_CENTER)
12087 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12088 else if (vp_playfield->align == ALIGN_RIGHT)
12089 vp_playfield->x += playfield_width - vp_playfield->width;
12091 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12092 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12093 else if (vp_playfield->valign == VALIGN_MIDDLE)
12094 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12095 else if (vp_playfield->valign == VALIGN_BOTTOM)
12096 vp_playfield->y += playfield_height - vp_playfield->height;
12098 vp_playfield->x += vp_playfield->margin_left;
12099 vp_playfield->y += vp_playfield->margin_top;
12101 // adjust individual playfield borders if only default border is specified
12103 if (vp_playfield->border_left == -1)
12104 vp_playfield->border_left = vp_playfield->border_size;
12105 if (vp_playfield->border_right == -1)
12106 vp_playfield->border_right = vp_playfield->border_size;
12107 if (vp_playfield->border_top == -1)
12108 vp_playfield->border_top = vp_playfield->border_size;
12109 if (vp_playfield->border_bottom == -1)
12110 vp_playfield->border_bottom = vp_playfield->border_size;
12112 // set dynamic playfield borders if borders are specified as undefined
12113 // (but only if window size was dynamic and playfield size was static)
12115 if (dynamic_window_width && !dynamic_playfield_width)
12117 if (vp_playfield->border_left == -1)
12119 vp_playfield->border_left = (vp_playfield->x -
12120 vp_playfield->margin_left);
12121 vp_playfield->x -= vp_playfield->border_left;
12122 vp_playfield->width += vp_playfield->border_left;
12125 if (vp_playfield->border_right == -1)
12127 vp_playfield->border_right = (vp_window->width -
12129 vp_playfield->width -
12130 vp_playfield->margin_right);
12131 vp_playfield->width += vp_playfield->border_right;
12135 if (dynamic_window_height && !dynamic_playfield_height)
12137 if (vp_playfield->border_top == -1)
12139 vp_playfield->border_top = (vp_playfield->y -
12140 vp_playfield->margin_top);
12141 vp_playfield->y -= vp_playfield->border_top;
12142 vp_playfield->height += vp_playfield->border_top;
12145 if (vp_playfield->border_bottom == -1)
12147 vp_playfield->border_bottom = (vp_window->height -
12149 vp_playfield->height -
12150 vp_playfield->margin_bottom);
12151 vp_playfield->height += vp_playfield->border_bottom;
12155 // adjust playfield size to be a multiple of a defined alignment tile size
12157 int align_size = vp_playfield->align_size;
12158 int playfield_xtiles = vp_playfield->width / align_size;
12159 int playfield_ytiles = vp_playfield->height / align_size;
12160 int playfield_width_corrected = playfield_xtiles * align_size;
12161 int playfield_height_corrected = playfield_ytiles * align_size;
12162 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12163 i == GFX_SPECIAL_ARG_EDITOR);
12165 if (is_playfield_mode &&
12166 dynamic_playfield_width &&
12167 vp_playfield->width != playfield_width_corrected)
12169 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12171 vp_playfield->width = playfield_width_corrected;
12173 if (vp_playfield->align == ALIGN_LEFT)
12175 vp_playfield->border_left += playfield_xdiff;
12177 else if (vp_playfield->align == ALIGN_RIGHT)
12179 vp_playfield->border_right += playfield_xdiff;
12181 else if (vp_playfield->align == ALIGN_CENTER)
12183 int border_left_diff = playfield_xdiff / 2;
12184 int border_right_diff = playfield_xdiff - border_left_diff;
12186 vp_playfield->border_left += border_left_diff;
12187 vp_playfield->border_right += border_right_diff;
12191 if (is_playfield_mode &&
12192 dynamic_playfield_height &&
12193 vp_playfield->height != playfield_height_corrected)
12195 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12197 vp_playfield->height = playfield_height_corrected;
12199 if (vp_playfield->valign == VALIGN_TOP)
12201 vp_playfield->border_top += playfield_ydiff;
12203 else if (vp_playfield->align == VALIGN_BOTTOM)
12205 vp_playfield->border_right += playfield_ydiff;
12207 else if (vp_playfield->align == VALIGN_MIDDLE)
12209 int border_top_diff = playfield_ydiff / 2;
12210 int border_bottom_diff = playfield_ydiff - border_top_diff;
12212 vp_playfield->border_top += border_top_diff;
12213 vp_playfield->border_bottom += border_bottom_diff;
12217 // adjust door positions according to specified alignment
12219 for (j = 0; j < 2; j++)
12221 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12223 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12224 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12225 else if (vp_door->align == ALIGN_CENTER)
12226 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12227 else if (vp_door->align == ALIGN_RIGHT)
12228 vp_door->x += vp_window->width - vp_door->width;
12230 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12231 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12232 else if (vp_door->valign == VALIGN_MIDDLE)
12233 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12234 else if (vp_door->valign == VALIGN_BOTTOM)
12235 vp_door->y += vp_window->height - vp_door->height;
12240 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12244 struct XYTileSize *dst, *src;
12247 editor_buttons_xy[] =
12250 &editor.button.element_left, &editor.palette.element_left,
12251 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12254 &editor.button.element_middle, &editor.palette.element_middle,
12255 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12258 &editor.button.element_right, &editor.palette.element_right,
12259 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12266 // set default position for element buttons to element graphics
12267 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12269 if ((*editor_buttons_xy[i].dst).x == -1 &&
12270 (*editor_buttons_xy[i].dst).y == -1)
12272 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12274 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12276 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12280 // adjust editor palette rows and columns if specified to be dynamic
12282 if (editor.palette.cols == -1)
12284 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12285 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12286 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12288 editor.palette.cols = (vp_width - sc_width) / bt_width;
12290 if (editor.palette.x == -1)
12292 int palette_width = editor.palette.cols * bt_width + sc_width;
12294 editor.palette.x = (vp_width - palette_width) / 2;
12298 if (editor.palette.rows == -1)
12300 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12301 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12302 int tx_height = getFontHeight(FONT_TEXT_2);
12304 editor.palette.rows = (vp_height - tx_height) / bt_height;
12306 if (editor.palette.y == -1)
12308 int palette_height = editor.palette.rows * bt_height + tx_height;
12310 editor.palette.y = (vp_height - palette_height) / 2;
12315 static void LoadMenuDesignSettingsFromFilename(char *filename)
12317 static struct TitleFadingInfo tfi;
12318 static struct TitleMessageInfo tmi;
12319 static struct TokenInfo title_tokens[] =
12321 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12322 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12323 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12324 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12325 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12329 static struct TokenInfo titlemessage_tokens[] =
12331 { TYPE_INTEGER, &tmi.x, ".x" },
12332 { TYPE_INTEGER, &tmi.y, ".y" },
12333 { TYPE_INTEGER, &tmi.width, ".width" },
12334 { TYPE_INTEGER, &tmi.height, ".height" },
12335 { TYPE_INTEGER, &tmi.chars, ".chars" },
12336 { TYPE_INTEGER, &tmi.lines, ".lines" },
12337 { TYPE_INTEGER, &tmi.align, ".align" },
12338 { TYPE_INTEGER, &tmi.valign, ".valign" },
12339 { TYPE_INTEGER, &tmi.font, ".font" },
12340 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12341 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12342 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12343 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12344 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12345 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12346 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12347 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12348 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12354 struct TitleFadingInfo *info;
12359 // initialize first titles from "enter screen" definitions, if defined
12360 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12361 { &title_first_default, "menu.enter_screen.TITLE" },
12363 // initialize title screens from "next screen" definitions, if defined
12364 { &title_initial_default, "menu.next_screen.TITLE" },
12365 { &title_default, "menu.next_screen.TITLE" },
12371 struct TitleMessageInfo *array;
12374 titlemessage_arrays[] =
12376 // initialize first titles from "enter screen" definitions, if defined
12377 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12378 { titlescreen_first, "menu.enter_screen.TITLE" },
12379 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12380 { titlemessage_first, "menu.enter_screen.TITLE" },
12382 // initialize titles from "next screen" definitions, if defined
12383 { titlescreen_initial, "menu.next_screen.TITLE" },
12384 { titlescreen, "menu.next_screen.TITLE" },
12385 { titlemessage_initial, "menu.next_screen.TITLE" },
12386 { titlemessage, "menu.next_screen.TITLE" },
12388 // overwrite titles with title definitions, if defined
12389 { titlescreen_initial_first, "[title_initial]" },
12390 { titlescreen_first, "[title]" },
12391 { titlemessage_initial_first, "[title_initial]" },
12392 { titlemessage_first, "[title]" },
12394 { titlescreen_initial, "[title_initial]" },
12395 { titlescreen, "[title]" },
12396 { titlemessage_initial, "[title_initial]" },
12397 { titlemessage, "[title]" },
12399 // overwrite titles with title screen/message definitions, if defined
12400 { titlescreen_initial_first, "[titlescreen_initial]" },
12401 { titlescreen_first, "[titlescreen]" },
12402 { titlemessage_initial_first, "[titlemessage_initial]" },
12403 { titlemessage_first, "[titlemessage]" },
12405 { titlescreen_initial, "[titlescreen_initial]" },
12406 { titlescreen, "[titlescreen]" },
12407 { titlemessage_initial, "[titlemessage_initial]" },
12408 { titlemessage, "[titlemessage]" },
12412 SetupFileHash *setup_file_hash;
12415 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12418 // the following initializes hierarchical values from dynamic configuration
12420 // special case: initialize with default values that may be overwritten
12421 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12422 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12424 struct TokenIntPtrInfo menu_config[] =
12426 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12427 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12428 { "menu.list_size", &menu.list_size[i] }
12431 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12433 char *token = menu_config[j].token;
12434 char *value = getHashEntry(setup_file_hash, token);
12437 *menu_config[j].value = get_integer_from_string(value);
12441 // special case: initialize with default values that may be overwritten
12442 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12443 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12445 struct TokenIntPtrInfo menu_config[] =
12447 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12448 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12449 { "menu.list_size.INFO", &menu.list_size_info[i] }
12452 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12454 char *token = menu_config[j].token;
12455 char *value = getHashEntry(setup_file_hash, token);
12458 *menu_config[j].value = get_integer_from_string(value);
12462 // special case: initialize with default values that may be overwritten
12463 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12464 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12466 struct TokenIntPtrInfo menu_config[] =
12468 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12469 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12472 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12474 char *token = menu_config[j].token;
12475 char *value = getHashEntry(setup_file_hash, token);
12478 *menu_config[j].value = get_integer_from_string(value);
12482 // special case: initialize with default values that may be overwritten
12483 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12484 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12486 struct TokenIntPtrInfo menu_config[] =
12488 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12489 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12490 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12491 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12492 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12493 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12494 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12495 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12496 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12499 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12501 char *token = menu_config[j].token;
12502 char *value = getHashEntry(setup_file_hash, token);
12505 *menu_config[j].value = get_integer_from_string(value);
12509 // special case: initialize with default values that may be overwritten
12510 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12511 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12513 struct TokenIntPtrInfo menu_config[] =
12515 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12516 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12517 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12518 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12519 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12520 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12521 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12522 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12523 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12526 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12528 char *token = menu_config[j].token;
12529 char *value = getHashEntry(setup_file_hash, token);
12532 *menu_config[j].value = get_token_parameter_value(token, value);
12536 // special case: initialize with default values that may be overwritten
12537 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12538 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12542 char *token_prefix;
12543 struct RectWithBorder *struct_ptr;
12547 { "viewport.window", &viewport.window[i] },
12548 { "viewport.playfield", &viewport.playfield[i] },
12549 { "viewport.door_1", &viewport.door_1[i] },
12550 { "viewport.door_2", &viewport.door_2[i] }
12553 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12555 struct TokenIntPtrInfo vp_config[] =
12557 { ".x", &vp_struct[j].struct_ptr->x },
12558 { ".y", &vp_struct[j].struct_ptr->y },
12559 { ".width", &vp_struct[j].struct_ptr->width },
12560 { ".height", &vp_struct[j].struct_ptr->height },
12561 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12562 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12563 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12564 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12565 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12566 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12567 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12568 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12569 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12570 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12571 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12572 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12573 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12574 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12575 { ".align", &vp_struct[j].struct_ptr->align },
12576 { ".valign", &vp_struct[j].struct_ptr->valign }
12579 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12581 char *token = getStringCat2(vp_struct[j].token_prefix,
12582 vp_config[k].token);
12583 char *value = getHashEntry(setup_file_hash, token);
12586 *vp_config[k].value = get_token_parameter_value(token, value);
12593 // special case: initialize with default values that may be overwritten
12594 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12595 for (i = 0; title_info[i].info != NULL; i++)
12597 struct TitleFadingInfo *info = title_info[i].info;
12598 char *base_token = title_info[i].text;
12600 for (j = 0; title_tokens[j].type != -1; j++)
12602 char *token = getStringCat2(base_token, title_tokens[j].text);
12603 char *value = getHashEntry(setup_file_hash, token);
12607 int parameter_value = get_token_parameter_value(token, value);
12611 *(int *)title_tokens[j].value = (int)parameter_value;
12620 // special case: initialize with default values that may be overwritten
12621 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12622 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12624 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12625 char *base_token = titlemessage_arrays[i].text;
12627 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12629 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12630 char *value = getHashEntry(setup_file_hash, token);
12634 int parameter_value = get_token_parameter_value(token, value);
12636 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12640 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12641 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12643 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12653 // special case: check if network and preview player positions are redefined,
12654 // to compare this later against the main menu level preview being redefined
12655 struct TokenIntPtrInfo menu_config_players[] =
12657 { "main.network_players.x", &menu.main.network_players.redefined },
12658 { "main.network_players.y", &menu.main.network_players.redefined },
12659 { "main.preview_players.x", &menu.main.preview_players.redefined },
12660 { "main.preview_players.y", &menu.main.preview_players.redefined },
12661 { "preview.x", &preview.redefined },
12662 { "preview.y", &preview.redefined }
12665 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12666 *menu_config_players[i].value = FALSE;
12668 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12669 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12670 *menu_config_players[i].value = TRUE;
12672 // read (and overwrite with) values that may be specified in config file
12673 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12675 freeSetupFileHash(setup_file_hash);
12678 void LoadMenuDesignSettings(void)
12680 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12682 InitMenuDesignSettings_Static();
12683 InitMenuDesignSettings_SpecialPreProcessing();
12685 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12687 // first look for special settings configured in level series config
12688 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12690 if (fileExists(filename_base))
12691 LoadMenuDesignSettingsFromFilename(filename_base);
12694 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12696 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12697 LoadMenuDesignSettingsFromFilename(filename_local);
12699 InitMenuDesignSettings_SpecialPostProcessing();
12702 void LoadMenuDesignSettings_AfterGraphics(void)
12704 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12707 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12709 char *filename = getEditorSetupFilename();
12710 SetupFileList *setup_file_list, *list;
12711 SetupFileHash *element_hash;
12712 int num_unknown_tokens = 0;
12715 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12718 element_hash = newSetupFileHash();
12720 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12721 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12723 // determined size may be larger than needed (due to unknown elements)
12725 for (list = setup_file_list; list != NULL; list = list->next)
12728 // add space for up to 3 more elements for padding that may be needed
12729 *num_elements += 3;
12731 // free memory for old list of elements, if needed
12732 checked_free(*elements);
12734 // allocate memory for new list of elements
12735 *elements = checked_malloc(*num_elements * sizeof(int));
12738 for (list = setup_file_list; list != NULL; list = list->next)
12740 char *value = getHashEntry(element_hash, list->token);
12742 if (value == NULL) // try to find obsolete token mapping
12744 char *mapped_token = get_mapped_token(list->token);
12746 if (mapped_token != NULL)
12748 value = getHashEntry(element_hash, mapped_token);
12750 free(mapped_token);
12756 (*elements)[(*num_elements)++] = atoi(value);
12760 if (num_unknown_tokens == 0)
12763 Warn("unknown token(s) found in config file:");
12764 Warn("- config file: '%s'", filename);
12766 num_unknown_tokens++;
12769 Warn("- token: '%s'", list->token);
12773 if (num_unknown_tokens > 0)
12776 while (*num_elements % 4) // pad with empty elements, if needed
12777 (*elements)[(*num_elements)++] = EL_EMPTY;
12779 freeSetupFileList(setup_file_list);
12780 freeSetupFileHash(element_hash);
12783 for (i = 0; i < *num_elements; i++)
12784 Debug("editor", "element '%s' [%d]\n",
12785 element_info[(*elements)[i]].token_name, (*elements)[i]);
12789 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12792 SetupFileHash *setup_file_hash = NULL;
12793 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12794 char *filename_music, *filename_prefix, *filename_info;
12800 token_to_value_ptr[] =
12802 { "title_header", &tmp_music_file_info.title_header },
12803 { "artist_header", &tmp_music_file_info.artist_header },
12804 { "album_header", &tmp_music_file_info.album_header },
12805 { "year_header", &tmp_music_file_info.year_header },
12807 { "title", &tmp_music_file_info.title },
12808 { "artist", &tmp_music_file_info.artist },
12809 { "album", &tmp_music_file_info.album },
12810 { "year", &tmp_music_file_info.year },
12816 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12817 getCustomMusicFilename(basename));
12819 if (filename_music == NULL)
12822 // ---------- try to replace file extension ----------
12824 filename_prefix = getStringCopy(filename_music);
12825 if (strrchr(filename_prefix, '.') != NULL)
12826 *strrchr(filename_prefix, '.') = '\0';
12827 filename_info = getStringCat2(filename_prefix, ".txt");
12829 if (fileExists(filename_info))
12830 setup_file_hash = loadSetupFileHash(filename_info);
12832 free(filename_prefix);
12833 free(filename_info);
12835 if (setup_file_hash == NULL)
12837 // ---------- try to add file extension ----------
12839 filename_prefix = getStringCopy(filename_music);
12840 filename_info = getStringCat2(filename_prefix, ".txt");
12842 if (fileExists(filename_info))
12843 setup_file_hash = loadSetupFileHash(filename_info);
12845 free(filename_prefix);
12846 free(filename_info);
12849 if (setup_file_hash == NULL)
12852 // ---------- music file info found ----------
12854 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12856 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12858 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12860 *token_to_value_ptr[i].value_ptr =
12861 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12864 tmp_music_file_info.basename = getStringCopy(basename);
12865 tmp_music_file_info.music = music;
12866 tmp_music_file_info.is_sound = is_sound;
12868 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12869 *new_music_file_info = tmp_music_file_info;
12871 return new_music_file_info;
12874 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12876 return get_music_file_info_ext(basename, music, FALSE);
12879 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12881 return get_music_file_info_ext(basename, sound, TRUE);
12884 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12885 char *basename, boolean is_sound)
12887 for (; list != NULL; list = list->next)
12888 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12894 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12896 return music_info_listed_ext(list, basename, FALSE);
12899 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12901 return music_info_listed_ext(list, basename, TRUE);
12904 void LoadMusicInfo(void)
12906 char *music_directory = getCustomMusicDirectory();
12907 int num_music = getMusicListSize();
12908 int num_music_noconf = 0;
12909 int num_sounds = getSoundListSize();
12911 DirectoryEntry *dir_entry;
12912 struct FileInfo *music, *sound;
12913 struct MusicFileInfo *next, **new;
12916 while (music_file_info != NULL)
12918 next = music_file_info->next;
12920 checked_free(music_file_info->basename);
12922 checked_free(music_file_info->title_header);
12923 checked_free(music_file_info->artist_header);
12924 checked_free(music_file_info->album_header);
12925 checked_free(music_file_info->year_header);
12927 checked_free(music_file_info->title);
12928 checked_free(music_file_info->artist);
12929 checked_free(music_file_info->album);
12930 checked_free(music_file_info->year);
12932 free(music_file_info);
12934 music_file_info = next;
12937 new = &music_file_info;
12939 for (i = 0; i < num_music; i++)
12941 music = getMusicListEntry(i);
12943 if (music->filename == NULL)
12946 if (strEqual(music->filename, UNDEFINED_FILENAME))
12949 // a configured file may be not recognized as music
12950 if (!FileIsMusic(music->filename))
12953 if (!music_info_listed(music_file_info, music->filename))
12955 *new = get_music_file_info(music->filename, i);
12958 new = &(*new)->next;
12962 if ((dir = openDirectory(music_directory)) == NULL)
12964 Warn("cannot read music directory '%s'", music_directory);
12969 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12971 char *basename = dir_entry->basename;
12972 boolean music_already_used = FALSE;
12975 // skip all music files that are configured in music config file
12976 for (i = 0; i < num_music; i++)
12978 music = getMusicListEntry(i);
12980 if (music->filename == NULL)
12983 if (strEqual(basename, music->filename))
12985 music_already_used = TRUE;
12990 if (music_already_used)
12993 if (!FileIsMusic(dir_entry->filename))
12996 if (!music_info_listed(music_file_info, basename))
12998 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
13001 new = &(*new)->next;
13004 num_music_noconf++;
13007 closeDirectory(dir);
13009 for (i = 0; i < num_sounds; i++)
13011 sound = getSoundListEntry(i);
13013 if (sound->filename == NULL)
13016 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13019 // a configured file may be not recognized as sound
13020 if (!FileIsSound(sound->filename))
13023 if (!sound_info_listed(music_file_info, sound->filename))
13025 *new = get_sound_file_info(sound->filename, i);
13027 new = &(*new)->next;
13031 // add pointers to previous list nodes
13033 struct MusicFileInfo *node = music_file_info;
13035 while (node != NULL)
13038 node->next->prev = node;
13044 static void add_helpanim_entry(int element, int action, int direction,
13045 int delay, int *num_list_entries)
13047 struct HelpAnimInfo *new_list_entry;
13048 (*num_list_entries)++;
13051 checked_realloc(helpanim_info,
13052 *num_list_entries * sizeof(struct HelpAnimInfo));
13053 new_list_entry = &helpanim_info[*num_list_entries - 1];
13055 new_list_entry->element = element;
13056 new_list_entry->action = action;
13057 new_list_entry->direction = direction;
13058 new_list_entry->delay = delay;
13061 static void print_unknown_token(char *filename, char *token, int token_nr)
13066 Warn("unknown token(s) found in config file:");
13067 Warn("- config file: '%s'", filename);
13070 Warn("- token: '%s'", token);
13073 static void print_unknown_token_end(int token_nr)
13079 void LoadHelpAnimInfo(void)
13081 char *filename = getHelpAnimFilename();
13082 SetupFileList *setup_file_list = NULL, *list;
13083 SetupFileHash *element_hash, *action_hash, *direction_hash;
13084 int num_list_entries = 0;
13085 int num_unknown_tokens = 0;
13088 if (fileExists(filename))
13089 setup_file_list = loadSetupFileList(filename);
13091 if (setup_file_list == NULL)
13093 // use reliable default values from static configuration
13094 SetupFileList *insert_ptr;
13096 insert_ptr = setup_file_list =
13097 newSetupFileList(helpanim_config[0].token,
13098 helpanim_config[0].value);
13100 for (i = 1; helpanim_config[i].token; i++)
13101 insert_ptr = addListEntry(insert_ptr,
13102 helpanim_config[i].token,
13103 helpanim_config[i].value);
13106 element_hash = newSetupFileHash();
13107 action_hash = newSetupFileHash();
13108 direction_hash = newSetupFileHash();
13110 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13111 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13113 for (i = 0; i < NUM_ACTIONS; i++)
13114 setHashEntry(action_hash, element_action_info[i].suffix,
13115 i_to_a(element_action_info[i].value));
13117 // do not store direction index (bit) here, but direction value!
13118 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13119 setHashEntry(direction_hash, element_direction_info[i].suffix,
13120 i_to_a(1 << element_direction_info[i].value));
13122 for (list = setup_file_list; list != NULL; list = list->next)
13124 char *element_token, *action_token, *direction_token;
13125 char *element_value, *action_value, *direction_value;
13126 int delay = atoi(list->value);
13128 if (strEqual(list->token, "end"))
13130 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13135 /* first try to break element into element/action/direction parts;
13136 if this does not work, also accept combined "element[.act][.dir]"
13137 elements (like "dynamite.active"), which are unique elements */
13139 if (strchr(list->token, '.') == NULL) // token contains no '.'
13141 element_value = getHashEntry(element_hash, list->token);
13142 if (element_value != NULL) // element found
13143 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13144 &num_list_entries);
13147 // no further suffixes found -- this is not an element
13148 print_unknown_token(filename, list->token, num_unknown_tokens++);
13154 // token has format "<prefix>.<something>"
13156 action_token = strchr(list->token, '.'); // suffix may be action ...
13157 direction_token = action_token; // ... or direction
13159 element_token = getStringCopy(list->token);
13160 *strchr(element_token, '.') = '\0';
13162 element_value = getHashEntry(element_hash, element_token);
13164 if (element_value == NULL) // this is no element
13166 element_value = getHashEntry(element_hash, list->token);
13167 if (element_value != NULL) // combined element found
13168 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13169 &num_list_entries);
13171 print_unknown_token(filename, list->token, num_unknown_tokens++);
13173 free(element_token);
13178 action_value = getHashEntry(action_hash, action_token);
13180 if (action_value != NULL) // action found
13182 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13183 &num_list_entries);
13185 free(element_token);
13190 direction_value = getHashEntry(direction_hash, direction_token);
13192 if (direction_value != NULL) // direction found
13194 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13195 &num_list_entries);
13197 free(element_token);
13202 if (strchr(action_token + 1, '.') == NULL)
13204 // no further suffixes found -- this is not an action nor direction
13206 element_value = getHashEntry(element_hash, list->token);
13207 if (element_value != NULL) // combined element found
13208 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13209 &num_list_entries);
13211 print_unknown_token(filename, list->token, num_unknown_tokens++);
13213 free(element_token);
13218 // token has format "<prefix>.<suffix>.<something>"
13220 direction_token = strchr(action_token + 1, '.');
13222 action_token = getStringCopy(action_token);
13223 *strchr(action_token + 1, '.') = '\0';
13225 action_value = getHashEntry(action_hash, action_token);
13227 if (action_value == NULL) // this is no action
13229 element_value = getHashEntry(element_hash, list->token);
13230 if (element_value != NULL) // combined element found
13231 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13232 &num_list_entries);
13234 print_unknown_token(filename, list->token, num_unknown_tokens++);
13236 free(element_token);
13237 free(action_token);
13242 direction_value = getHashEntry(direction_hash, direction_token);
13244 if (direction_value != NULL) // direction found
13246 add_helpanim_entry(atoi(element_value), atoi(action_value),
13247 atoi(direction_value), delay, &num_list_entries);
13249 free(element_token);
13250 free(action_token);
13255 // this is no direction
13257 element_value = getHashEntry(element_hash, list->token);
13258 if (element_value != NULL) // combined element found
13259 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13260 &num_list_entries);
13262 print_unknown_token(filename, list->token, num_unknown_tokens++);
13264 free(element_token);
13265 free(action_token);
13268 print_unknown_token_end(num_unknown_tokens);
13270 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13271 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13273 freeSetupFileList(setup_file_list);
13274 freeSetupFileHash(element_hash);
13275 freeSetupFileHash(action_hash);
13276 freeSetupFileHash(direction_hash);
13279 for (i = 0; i < num_list_entries; i++)
13280 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13281 EL_NAME(helpanim_info[i].element),
13282 helpanim_info[i].element,
13283 helpanim_info[i].action,
13284 helpanim_info[i].direction,
13285 helpanim_info[i].delay);
13289 void LoadHelpTextInfo(void)
13291 char *filename = getHelpTextFilename();
13294 if (helptext_info != NULL)
13296 freeSetupFileHash(helptext_info);
13297 helptext_info = NULL;
13300 if (fileExists(filename))
13301 helptext_info = loadSetupFileHash(filename);
13303 if (helptext_info == NULL)
13305 // use reliable default values from static configuration
13306 helptext_info = newSetupFileHash();
13308 for (i = 0; helptext_config[i].token; i++)
13309 setHashEntry(helptext_info,
13310 helptext_config[i].token,
13311 helptext_config[i].value);
13315 BEGIN_HASH_ITERATION(helptext_info, itr)
13317 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13318 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13320 END_HASH_ITERATION(hash, itr)
13325 // ----------------------------------------------------------------------------
13327 // ----------------------------------------------------------------------------
13329 #define MAX_NUM_CONVERT_LEVELS 1000
13331 void ConvertLevels(void)
13333 static LevelDirTree *convert_leveldir = NULL;
13334 static int convert_level_nr = -1;
13335 static int num_levels_handled = 0;
13336 static int num_levels_converted = 0;
13337 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13340 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13341 global.convert_leveldir);
13343 if (convert_leveldir == NULL)
13344 Fail("no such level identifier: '%s'", global.convert_leveldir);
13346 leveldir_current = convert_leveldir;
13348 if (global.convert_level_nr != -1)
13350 convert_leveldir->first_level = global.convert_level_nr;
13351 convert_leveldir->last_level = global.convert_level_nr;
13354 convert_level_nr = convert_leveldir->first_level;
13356 PrintLine("=", 79);
13357 Print("Converting levels\n");
13358 PrintLine("-", 79);
13359 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13360 Print("Level series name: '%s'\n", convert_leveldir->name);
13361 Print("Level series author: '%s'\n", convert_leveldir->author);
13362 Print("Number of levels: %d\n", convert_leveldir->levels);
13363 PrintLine("=", 79);
13366 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13367 levels_failed[i] = FALSE;
13369 while (convert_level_nr <= convert_leveldir->last_level)
13371 char *level_filename;
13374 level_nr = convert_level_nr++;
13376 Print("Level %03d: ", level_nr);
13378 LoadLevel(level_nr);
13379 if (level.no_level_file || level.no_valid_file)
13381 Print("(no level)\n");
13385 Print("converting level ... ");
13388 // special case: conversion of some EMC levels as requested by ACME
13389 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13392 level_filename = getDefaultLevelFilename(level_nr);
13393 new_level = !fileExists(level_filename);
13397 SaveLevel(level_nr);
13399 num_levels_converted++;
13401 Print("converted.\n");
13405 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13406 levels_failed[level_nr] = TRUE;
13408 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13411 num_levels_handled++;
13415 PrintLine("=", 79);
13416 Print("Number of levels handled: %d\n", num_levels_handled);
13417 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13418 (num_levels_handled ?
13419 num_levels_converted * 100 / num_levels_handled : 0));
13420 PrintLine("-", 79);
13421 Print("Summary (for automatic parsing by scripts):\n");
13422 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13423 convert_leveldir->identifier, num_levels_converted,
13424 num_levels_handled,
13425 (num_levels_handled ?
13426 num_levels_converted * 100 / num_levels_handled : 0));
13428 if (num_levels_handled != num_levels_converted)
13430 Print(", FAILED:");
13431 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13432 if (levels_failed[i])
13437 PrintLine("=", 79);
13439 CloseAllAndExit(0);
13443 // ----------------------------------------------------------------------------
13444 // create and save images for use in level sketches (raw BMP format)
13445 // ----------------------------------------------------------------------------
13447 void CreateLevelSketchImages(void)
13453 InitElementPropertiesGfxElement();
13455 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13456 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13458 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13460 int element = getMappedElement(i);
13461 char basename1[16];
13462 char basename2[16];
13466 sprintf(basename1, "%04d.bmp", i);
13467 sprintf(basename2, "%04ds.bmp", i);
13469 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13470 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13472 DrawSizedElement(0, 0, element, TILESIZE);
13473 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13475 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13476 Fail("cannot save level sketch image file '%s'", filename1);
13478 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13479 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13481 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13482 Fail("cannot save level sketch image file '%s'", filename2);
13487 // create corresponding SQL statements (for normal and small images)
13490 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13491 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13494 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13495 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13497 // optional: create content for forum level sketch demonstration post
13499 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13502 FreeBitmap(bitmap1);
13503 FreeBitmap(bitmap2);
13506 fprintf(stderr, "\n");
13508 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13510 CloseAllAndExit(0);
13514 // ----------------------------------------------------------------------------
13515 // create and save images for element collecting animations (raw BMP format)
13516 // ----------------------------------------------------------------------------
13518 static boolean createCollectImage(int element)
13520 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13523 void CreateCollectElementImages(void)
13527 int anim_frames = num_steps - 1;
13528 int tile_size = TILESIZE;
13529 int anim_width = tile_size * anim_frames;
13530 int anim_height = tile_size;
13531 int num_collect_images = 0;
13532 int pos_collect_images = 0;
13534 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13535 if (createCollectImage(i))
13536 num_collect_images++;
13538 Info("Creating %d element collecting animation images ...",
13539 num_collect_images);
13541 int dst_width = anim_width * 2;
13542 int dst_height = anim_height * num_collect_images / 2;
13543 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13544 char *basename_bmp = "RocksCollect.bmp";
13545 char *basename_png = "RocksCollect.png";
13546 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13547 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13548 int len_filename_bmp = strlen(filename_bmp);
13549 int len_filename_png = strlen(filename_png);
13550 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13551 char cmd_convert[max_command_len];
13553 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13557 // force using RGBA surface for destination bitmap
13558 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13559 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13561 dst_bitmap->surface =
13562 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13564 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13566 if (!createCollectImage(i))
13569 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13570 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13571 int graphic = el2img(i);
13572 char *token_name = element_info[i].token_name;
13573 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13574 Bitmap *src_bitmap;
13577 Info("- creating collecting image for '%s' ...", token_name);
13579 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13581 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13582 tile_size, tile_size, 0, 0);
13584 // force using RGBA surface for temporary bitmap (using transparent black)
13585 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13586 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13588 tmp_bitmap->surface =
13589 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13591 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13593 for (j = 0; j < anim_frames; j++)
13595 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13596 int frame_size = frame_size_final * num_steps;
13597 int offset = (tile_size - frame_size_final) / 2;
13598 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13600 while (frame_size > frame_size_final)
13604 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13606 FreeBitmap(frame_bitmap);
13608 frame_bitmap = half_bitmap;
13611 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13612 frame_size_final, frame_size_final,
13613 dst_x + j * tile_size + offset, dst_y + offset);
13615 FreeBitmap(frame_bitmap);
13618 tmp_bitmap->surface_masked = NULL;
13620 FreeBitmap(tmp_bitmap);
13622 pos_collect_images++;
13625 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13626 Fail("cannot save element collecting image file '%s'", filename_bmp);
13628 FreeBitmap(dst_bitmap);
13630 Info("Converting image file from BMP to PNG ...");
13632 if (system(cmd_convert) != 0)
13633 Fail("converting image file failed");
13635 unlink(filename_bmp);
13639 CloseAllAndExit(0);
13643 // ----------------------------------------------------------------------------
13644 // create and save images for custom and group elements (raw BMP format)
13645 // ----------------------------------------------------------------------------
13647 void CreateCustomElementImages(char *directory)
13649 char *src_basename = "RocksCE-template.ilbm";
13650 char *dst_basename = "RocksCE.bmp";
13651 char *src_filename = getPath2(directory, src_basename);
13652 char *dst_filename = getPath2(directory, dst_basename);
13653 Bitmap *src_bitmap;
13655 int yoffset_ce = 0;
13656 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13659 InitVideoDefaults();
13661 ReCreateBitmap(&backbuffer, video.width, video.height);
13663 src_bitmap = LoadImage(src_filename);
13665 bitmap = CreateBitmap(TILEX * 16 * 2,
13666 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13669 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13676 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13677 TILEX * x, TILEY * y + yoffset_ce);
13679 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13681 TILEX * x + TILEX * 16,
13682 TILEY * y + yoffset_ce);
13684 for (j = 2; j >= 0; j--)
13688 BlitBitmap(src_bitmap, bitmap,
13689 TILEX + c * 7, 0, 6, 10,
13690 TILEX * x + 6 + j * 7,
13691 TILEY * y + 11 + yoffset_ce);
13693 BlitBitmap(src_bitmap, bitmap,
13694 TILEX + c * 8, TILEY, 6, 10,
13695 TILEX * 16 + TILEX * x + 6 + j * 8,
13696 TILEY * y + 10 + yoffset_ce);
13702 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13709 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13710 TILEX * x, TILEY * y + yoffset_ge);
13712 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13714 TILEX * x + TILEX * 16,
13715 TILEY * y + yoffset_ge);
13717 for (j = 1; j >= 0; j--)
13721 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13722 TILEX * x + 6 + j * 10,
13723 TILEY * y + 11 + yoffset_ge);
13725 BlitBitmap(src_bitmap, bitmap,
13726 TILEX + c * 8, TILEY + 12, 6, 10,
13727 TILEX * 16 + TILEX * x + 10 + j * 8,
13728 TILEY * y + 10 + yoffset_ge);
13734 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13735 Fail("cannot save CE graphics file '%s'", dst_filename);
13737 FreeBitmap(bitmap);
13739 CloseAllAndExit(0);