1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
187 -1, SAVE_CONF_ALWAYS,
188 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
194 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
201 &li.use_step_counter, FALSE
206 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
207 &li.wind_direction_initial, MV_NONE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
213 &li.em_slippery_gems, FALSE
218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
219 &li.use_custom_template, FALSE
224 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
225 &li.can_move_into_acid_bits, ~0 // default: everything can
230 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
231 &li.dont_collide_with_bits, ~0 // default: always deadly
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
237 &li.em_explodes_by_fire, FALSE
242 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
243 &li.score[SC_TIME_BONUS], 1
248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
249 &li.auto_exit_sokoban, FALSE
254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
255 &li.auto_count_gems, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
261 &li.solved_by_one_player, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
267 &li.time_score_base, 1
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
273 &li.rate_time_over_score, FALSE
283 static struct LevelFileConfigInfo chunk_config_ELEM[] =
285 // (these values are the same for each player)
288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
289 &li.block_last_field, FALSE // default case for EM levels
293 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
294 &li.sp_block_last_field, TRUE // default case for SP levels
298 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
299 &li.instant_relocation, FALSE
303 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
304 &li.can_pass_to_walkable, FALSE
308 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
309 &li.block_snap_field, TRUE
313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
314 &li.continuous_snapping, TRUE
318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
319 &li.shifted_relocation, FALSE
323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
324 &li.lazy_relocation, FALSE
328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
329 &li.finish_dig_collect, TRUE
333 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
334 &li.keep_walkable_ce, FALSE
337 // (these values are different for each player)
340 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
341 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
346 &li.initial_player_gravity[0], FALSE
350 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
351 &li.use_start_element[0], FALSE
355 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
356 &li.start_element[0], EL_PLAYER_1
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
361 &li.use_artwork_element[0], FALSE
365 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
366 &li.artwork_element[0], EL_PLAYER_1
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
371 &li.use_explosion_element[0], FALSE
375 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
376 &li.explosion_element[0], EL_PLAYER_1
380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
381 &li.use_initial_inventory[0], FALSE
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
386 &li.initial_inventory_size[0], 1
390 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
391 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
392 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
397 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
398 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
403 &li.initial_player_gravity[1], FALSE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
408 &li.use_start_element[1], FALSE
412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
413 &li.start_element[1], EL_PLAYER_2
417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
418 &li.use_artwork_element[1], FALSE
422 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
423 &li.artwork_element[1], EL_PLAYER_2
427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
428 &li.use_explosion_element[1], FALSE
432 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
433 &li.explosion_element[1], EL_PLAYER_2
437 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
438 &li.use_initial_inventory[1], FALSE
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
443 &li.initial_inventory_size[1], 1
447 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
448 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
449 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
454 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
455 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
460 &li.initial_player_gravity[2], FALSE
464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
465 &li.use_start_element[2], FALSE
469 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
470 &li.start_element[2], EL_PLAYER_3
474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
475 &li.use_artwork_element[2], FALSE
479 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
480 &li.artwork_element[2], EL_PLAYER_3
484 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
485 &li.use_explosion_element[2], FALSE
489 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
490 &li.explosion_element[2], EL_PLAYER_3
494 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
495 &li.use_initial_inventory[2], FALSE
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
500 &li.initial_inventory_size[2], 1
504 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
505 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
506 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
511 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
512 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
517 &li.initial_player_gravity[3], FALSE
521 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
522 &li.use_start_element[3], FALSE
526 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
527 &li.start_element[3], EL_PLAYER_4
531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
532 &li.use_artwork_element[3], FALSE
536 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
537 &li.artwork_element[3], EL_PLAYER_4
541 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
542 &li.use_explosion_element[3], FALSE
546 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
547 &li.explosion_element[3], EL_PLAYER_4
551 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
552 &li.use_initial_inventory[3], FALSE
556 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
557 &li.initial_inventory_size[3], 1
561 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
562 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
563 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
568 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
569 &li.score[SC_EMERALD], 10
574 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
575 &li.score[SC_DIAMOND], 10
580 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
581 &li.score[SC_BUG], 10
586 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
587 &li.score[SC_SPACESHIP], 10
592 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
593 &li.score[SC_PACMAN], 10
598 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
599 &li.score[SC_NUT], 10
604 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
605 &li.score[SC_DYNAMITE], 10
610 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
611 &li.score[SC_KEY], 10
616 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
617 &li.score[SC_PEARL], 10
622 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
623 &li.score[SC_CRYSTAL], 10
628 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
629 &li.amoeba_content, EL_DIAMOND
633 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
638 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
639 &li.grow_into_diggable, TRUE
644 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
645 &li.yamyam_content, EL_ROCK, NULL,
646 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
650 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
651 &li.score[SC_YAMYAM], 10
656 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
657 &li.score[SC_ROBOT], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.time_magic_wall, 10
679 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
680 &li.game_of_life[0], 2
684 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
685 &li.game_of_life[1], 3
689 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
690 &li.game_of_life[2], 3
694 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
695 &li.game_of_life[3], 3
699 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
700 &li.use_life_bugs, FALSE
705 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
710 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
715 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
720 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
725 EL_TIMEGATE_SWITCH, -1,
726 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 &li.time_timegate, 10
731 EL_LIGHT_SWITCH_ACTIVE, -1,
732 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
737 EL_SHIELD_NORMAL, -1,
738 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 &li.shield_normal_time, 10
742 EL_SHIELD_NORMAL, -1,
743 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
744 &li.score[SC_SHIELD], 10
748 EL_SHIELD_DEADLY, -1,
749 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
750 &li.shield_deadly_time, 10
753 EL_SHIELD_DEADLY, -1,
754 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
755 &li.score[SC_SHIELD], 10
760 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
765 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
766 &li.extra_time_score, 10
770 EL_TIME_ORB_FULL, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 &li.time_orb_time, 10
775 EL_TIME_ORB_FULL, -1,
776 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
777 &li.use_time_orb_bug, FALSE
782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
783 &li.use_spring_bug, FALSE
788 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
789 &li.android_move_time, 10
793 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
794 &li.android_clone_time, 10
797 EL_EMC_ANDROID, SAVE_CONF_NEVER,
798 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
799 &li.android_clone_element[0], EL_EMPTY, NULL,
800 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
804 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
805 &li.android_clone_element[0], EL_EMPTY, NULL,
806 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
811 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
816 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
821 EL_EMC_MAGNIFIER, -1,
822 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
823 &li.magnify_score, 10
826 EL_EMC_MAGNIFIER, -1,
827 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
832 EL_EMC_MAGIC_BALL, -1,
833 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
837 EL_EMC_MAGIC_BALL, -1,
838 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
839 &li.ball_random, FALSE
842 EL_EMC_MAGIC_BALL, -1,
843 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
844 &li.ball_active_initial, FALSE
847 EL_EMC_MAGIC_BALL, -1,
848 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
849 &li.ball_content, EL_EMPTY, NULL,
850 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
854 EL_SOKOBAN_FIELD_EMPTY, -1,
855 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
856 &li.sb_fields_needed, TRUE
860 EL_SOKOBAN_OBJECT, -1,
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
862 &li.sb_objects_needed, TRUE
867 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
868 &li.mm_laser_red, FALSE
872 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
873 &li.mm_laser_green, FALSE
877 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
878 &li.mm_laser_blue, TRUE
883 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
884 &li.df_laser_red, TRUE
888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
889 &li.df_laser_green, TRUE
893 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
894 &li.df_laser_blue, FALSE
898 EL_MM_FUSE_ACTIVE, -1,
899 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
904 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
916 &li.mm_ball_choice_mode, ANIM_RANDOM
920 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
921 &li.mm_ball_content, EL_EMPTY, NULL,
922 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
926 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
927 &li.rotate_mm_ball_content, TRUE
931 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
932 &li.explode_mm_ball, FALSE
936 EL_MM_STEEL_BLOCK, -1,
937 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
938 &li.mm_time_block, 75
942 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
943 &li.score[SC_ELEM_BONUS], 10
946 // ---------- unused values -------------------------------------------------
949 EL_UNKNOWN, SAVE_CONF_NEVER,
950 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
951 &li.score[SC_UNKNOWN_15], 10
961 static struct LevelFileConfigInfo chunk_config_NOTE[] =
965 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
966 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
970 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
971 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
977 &xx_envelope.autowrap, FALSE
981 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
982 &xx_envelope.centered, FALSE
987 TYPE_STRING, CONF_VALUE_BYTES(1),
988 &xx_envelope.text, -1, NULL,
989 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
990 &xx_default_string_empty[0]
1000 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1004 TYPE_STRING, CONF_VALUE_BYTES(1),
1005 &xx_ei.description[0], -1,
1006 &yy_ei.description[0],
1007 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1008 &xx_default_description[0]
1013 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1014 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1015 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1017 #if ENABLE_RESERVED_CODE
1018 // (reserved for later use)
1021 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1022 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1023 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1029 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1030 &xx_ei.use_gfx_element, FALSE,
1031 &yy_ei.use_gfx_element
1035 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1036 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1037 &yy_ei.gfx_element_initial
1042 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1043 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1044 &yy_ei.access_direction
1049 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1050 &xx_ei.collect_score_initial, 10,
1051 &yy_ei.collect_score_initial
1055 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1056 &xx_ei.collect_count_initial, 1,
1057 &yy_ei.collect_count_initial
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1063 &xx_ei.ce_value_fixed_initial, 0,
1064 &yy_ei.ce_value_fixed_initial
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1069 &xx_ei.ce_value_random_initial, 0,
1070 &yy_ei.ce_value_random_initial
1074 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1075 &xx_ei.use_last_ce_value, FALSE,
1076 &yy_ei.use_last_ce_value
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1082 &xx_ei.push_delay_fixed, 8,
1083 &yy_ei.push_delay_fixed
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1088 &xx_ei.push_delay_random, 8,
1089 &yy_ei.push_delay_random
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1094 &xx_ei.drop_delay_fixed, 0,
1095 &yy_ei.drop_delay_fixed
1099 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1100 &xx_ei.drop_delay_random, 0,
1101 &yy_ei.drop_delay_random
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1106 &xx_ei.move_delay_fixed, 0,
1107 &yy_ei.move_delay_fixed
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1112 &xx_ei.move_delay_random, 0,
1113 &yy_ei.move_delay_random
1117 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1118 &xx_ei.step_delay_fixed, 0,
1119 &yy_ei.step_delay_fixed
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1124 &xx_ei.step_delay_random, 0,
1125 &yy_ei.step_delay_random
1130 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1131 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1136 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1137 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1138 &yy_ei.move_direction_initial
1142 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1143 &xx_ei.move_stepsize, TILEX / 8,
1144 &yy_ei.move_stepsize
1149 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1150 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1151 &yy_ei.move_enter_element
1155 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1156 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1157 &yy_ei.move_leave_element
1161 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1162 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1163 &yy_ei.move_leave_type
1168 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1169 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1170 &yy_ei.slippery_type
1175 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1176 &xx_ei.explosion_type, EXPLODES_3X3,
1177 &yy_ei.explosion_type
1181 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1182 &xx_ei.explosion_delay, 16,
1183 &yy_ei.explosion_delay
1187 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1188 &xx_ei.ignition_delay, 8,
1189 &yy_ei.ignition_delay
1194 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1195 &xx_ei.content, EL_EMPTY_SPACE,
1197 &xx_num_contents, 1, 1
1200 // ---------- "num_change_pages" must be the last entry ---------------------
1203 -1, SAVE_CONF_ALWAYS,
1204 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1205 &xx_ei.num_change_pages, 1,
1206 &yy_ei.num_change_pages
1217 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1219 // ---------- "current_change_page" must be the first entry -----------------
1222 -1, SAVE_CONF_ALWAYS,
1223 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1224 &xx_current_change_page, -1
1227 // ---------- (the remaining entries can be in any order) -------------------
1231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1232 &xx_change.can_change, FALSE
1237 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1238 &xx_event_bits[0], 0
1242 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1243 &xx_event_bits[1], 0
1248 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1249 &xx_change.trigger_player, CH_PLAYER_ANY
1253 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1254 &xx_change.trigger_side, CH_SIDE_ANY
1258 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1259 &xx_change.trigger_page, CH_PAGE_ANY
1264 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1265 &xx_change.target_element, EL_EMPTY_SPACE
1270 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1271 &xx_change.delay_fixed, 0
1275 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1276 &xx_change.delay_random, 0
1280 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1281 &xx_change.delay_frames, FRAMES_PER_SECOND
1286 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1287 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1293 &xx_change.explode, FALSE
1297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1298 &xx_change.use_target_content, FALSE
1302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1303 &xx_change.only_if_complete, FALSE
1307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1308 &xx_change.use_random_replace, FALSE
1312 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1313 &xx_change.random_percentage, 100
1317 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1318 &xx_change.replace_when, CP_WHEN_EMPTY
1323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1324 &xx_change.has_action, FALSE
1328 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1329 &xx_change.action_type, CA_NO_ACTION
1333 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1334 &xx_change.action_mode, CA_MODE_UNDEFINED
1338 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1339 &xx_change.action_arg, CA_ARG_UNDEFINED
1344 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1345 &xx_change.action_element, EL_EMPTY_SPACE
1350 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1351 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1352 &xx_num_contents, 1, 1
1362 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1366 TYPE_STRING, CONF_VALUE_BYTES(1),
1367 &xx_ei.description[0], -1, NULL,
1368 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1369 &xx_default_description[0]
1374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1375 &xx_ei.use_gfx_element, FALSE
1379 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1380 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1385 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1386 &xx_group.choice_mode, ANIM_RANDOM
1391 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1392 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1393 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1403 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1408 &xx_ei.use_gfx_element, FALSE
1412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1413 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1423 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1428 &li.block_snap_field, TRUE
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1433 &li.continuous_snapping, TRUE
1437 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1438 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1443 &li.use_start_element[0], FALSE
1447 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1448 &li.start_element[0], EL_PLAYER_1
1452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1453 &li.use_artwork_element[0], FALSE
1457 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1458 &li.artwork_element[0], EL_PLAYER_1
1462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1463 &li.use_explosion_element[0], FALSE
1467 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1468 &li.explosion_element[0], EL_PLAYER_1
1483 filetype_id_list[] =
1485 { LEVEL_FILE_TYPE_RND, "RND" },
1486 { LEVEL_FILE_TYPE_BD, "BD" },
1487 { LEVEL_FILE_TYPE_EM, "EM" },
1488 { LEVEL_FILE_TYPE_SP, "SP" },
1489 { LEVEL_FILE_TYPE_DX, "DX" },
1490 { LEVEL_FILE_TYPE_SB, "SB" },
1491 { LEVEL_FILE_TYPE_DC, "DC" },
1492 { LEVEL_FILE_TYPE_MM, "MM" },
1493 { LEVEL_FILE_TYPE_MM, "DF" },
1498 // ============================================================================
1499 // level file functions
1500 // ============================================================================
1502 static boolean check_special_flags(char *flag)
1504 if (strEqual(options.special_flags, flag) ||
1505 strEqual(leveldir_current->special_flags, flag))
1511 static struct DateInfo getCurrentDate(void)
1513 time_t epoch_seconds = time(NULL);
1514 struct tm *now = localtime(&epoch_seconds);
1515 struct DateInfo date;
1517 date.year = now->tm_year + 1900;
1518 date.month = now->tm_mon + 1;
1519 date.day = now->tm_mday;
1521 date.src = DATE_SRC_CLOCK;
1526 static void resetEventFlags(struct ElementChangeInfo *change)
1530 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1531 change->has_event[i] = FALSE;
1534 static void resetEventBits(void)
1538 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1539 xx_event_bits[i] = 0;
1542 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1546 /* important: only change event flag if corresponding event bit is set
1547 (this is because all xx_event_bits[] values are loaded separately,
1548 and all xx_event_bits[] values are set back to zero before loading
1549 another value xx_event_bits[x] (each value representing 32 flags)) */
1551 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1552 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1553 change->has_event[i] = TRUE;
1556 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1560 /* in contrast to the above function setEventFlagsFromEventBits(), it
1561 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1562 depending on the corresponding change->has_event[i] values here, as
1563 all xx_event_bits[] values are reset in resetEventBits() before */
1565 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1566 if (change->has_event[i])
1567 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1570 static char *getDefaultElementDescription(struct ElementInfo *ei)
1572 static char description[MAX_ELEMENT_NAME_LEN + 1];
1573 char *default_description = (ei->custom_description != NULL ?
1574 ei->custom_description :
1575 ei->editor_description);
1578 // always start with reliable default values
1579 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1580 description[i] = '\0';
1582 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1583 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1585 return &description[0];
1588 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1590 char *default_description = getDefaultElementDescription(ei);
1593 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1594 ei->description[i] = default_description[i];
1597 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1601 for (i = 0; conf[i].data_type != -1; i++)
1603 int default_value = conf[i].default_value;
1604 int data_type = conf[i].data_type;
1605 int conf_type = conf[i].conf_type;
1606 int byte_mask = conf_type & CONF_MASK_BYTES;
1608 if (byte_mask == CONF_MASK_MULTI_BYTES)
1610 int default_num_entities = conf[i].default_num_entities;
1611 int max_num_entities = conf[i].max_num_entities;
1613 *(int *)(conf[i].num_entities) = default_num_entities;
1615 if (data_type == TYPE_STRING)
1617 char *default_string = conf[i].default_string;
1618 char *string = (char *)(conf[i].value);
1620 strncpy(string, default_string, max_num_entities);
1622 else if (data_type == TYPE_ELEMENT_LIST)
1624 int *element_array = (int *)(conf[i].value);
1627 for (j = 0; j < max_num_entities; j++)
1628 element_array[j] = default_value;
1630 else if (data_type == TYPE_CONTENT_LIST)
1632 struct Content *content = (struct Content *)(conf[i].value);
1635 for (c = 0; c < max_num_entities; c++)
1636 for (y = 0; y < 3; y++)
1637 for (x = 0; x < 3; x++)
1638 content[c].e[x][y] = default_value;
1641 else // constant size configuration data (1, 2 or 4 bytes)
1643 if (data_type == TYPE_BOOLEAN)
1644 *(boolean *)(conf[i].value) = default_value;
1646 *(int *) (conf[i].value) = default_value;
1651 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1655 for (i = 0; conf[i].data_type != -1; i++)
1657 int data_type = conf[i].data_type;
1658 int conf_type = conf[i].conf_type;
1659 int byte_mask = conf_type & CONF_MASK_BYTES;
1661 if (byte_mask == CONF_MASK_MULTI_BYTES)
1663 int max_num_entities = conf[i].max_num_entities;
1665 if (data_type == TYPE_STRING)
1667 char *string = (char *)(conf[i].value);
1668 char *string_copy = (char *)(conf[i].value_copy);
1670 strncpy(string_copy, string, max_num_entities);
1672 else if (data_type == TYPE_ELEMENT_LIST)
1674 int *element_array = (int *)(conf[i].value);
1675 int *element_array_copy = (int *)(conf[i].value_copy);
1678 for (j = 0; j < max_num_entities; j++)
1679 element_array_copy[j] = element_array[j];
1681 else if (data_type == TYPE_CONTENT_LIST)
1683 struct Content *content = (struct Content *)(conf[i].value);
1684 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1687 for (c = 0; c < max_num_entities; c++)
1688 for (y = 0; y < 3; y++)
1689 for (x = 0; x < 3; x++)
1690 content_copy[c].e[x][y] = content[c].e[x][y];
1693 else // constant size configuration data (1, 2 or 4 bytes)
1695 if (data_type == TYPE_BOOLEAN)
1696 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1698 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1703 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1707 xx_ei = *ei_from; // copy element data into temporary buffer
1708 yy_ei = *ei_to; // copy element data into temporary buffer
1710 copyConfigFromConfigList(chunk_config_CUSX_base);
1715 // ---------- reinitialize and copy change pages ----------
1717 ei_to->num_change_pages = ei_from->num_change_pages;
1718 ei_to->current_change_page = ei_from->current_change_page;
1720 setElementChangePages(ei_to, ei_to->num_change_pages);
1722 for (i = 0; i < ei_to->num_change_pages; i++)
1723 ei_to->change_page[i] = ei_from->change_page[i];
1725 // ---------- copy group element info ----------
1726 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1727 *ei_to->group = *ei_from->group;
1729 // mark this custom element as modified
1730 ei_to->modified_settings = TRUE;
1733 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1735 int change_page_size = sizeof(struct ElementChangeInfo);
1737 ei->num_change_pages = MAX(1, change_pages);
1740 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1742 if (ei->current_change_page >= ei->num_change_pages)
1743 ei->current_change_page = ei->num_change_pages - 1;
1745 ei->change = &ei->change_page[ei->current_change_page];
1748 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1750 xx_change = *change; // copy change data into temporary buffer
1752 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1754 *change = xx_change;
1756 resetEventFlags(change);
1758 change->direct_action = 0;
1759 change->other_action = 0;
1761 change->pre_change_function = NULL;
1762 change->change_function = NULL;
1763 change->post_change_function = NULL;
1766 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1770 li = *level; // copy level data into temporary buffer
1771 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1772 *level = li; // copy temporary buffer back to level data
1774 setLevelInfoToDefaults_BD();
1775 setLevelInfoToDefaults_EM();
1776 setLevelInfoToDefaults_SP();
1777 setLevelInfoToDefaults_MM();
1779 level->native_bd_level = &native_bd_level;
1780 level->native_em_level = &native_em_level;
1781 level->native_sp_level = &native_sp_level;
1782 level->native_mm_level = &native_mm_level;
1784 level->file_version = FILE_VERSION_ACTUAL;
1785 level->game_version = GAME_VERSION_ACTUAL;
1787 level->creation_date = getCurrentDate();
1789 level->encoding_16bit_field = TRUE;
1790 level->encoding_16bit_yamyam = TRUE;
1791 level->encoding_16bit_amoeba = TRUE;
1793 // clear level name and level author string buffers
1794 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1795 level->name[i] = '\0';
1796 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1797 level->author[i] = '\0';
1799 // set level name and level author to default values
1800 strcpy(level->name, NAMELESS_LEVEL_NAME);
1801 strcpy(level->author, ANONYMOUS_NAME);
1803 // set level playfield to playable default level with player and exit
1804 for (x = 0; x < MAX_LEV_FIELDX; x++)
1805 for (y = 0; y < MAX_LEV_FIELDY; y++)
1806 level->field[x][y] = EL_SAND;
1808 level->field[0][0] = EL_PLAYER_1;
1809 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1811 BorderElement = EL_STEELWALL;
1813 // detect custom elements when loading them
1814 level->file_has_custom_elements = FALSE;
1816 // set all bug compatibility flags to "false" => do not emulate this bug
1817 level->use_action_after_change_bug = FALSE;
1819 if (leveldir_current)
1821 // try to determine better author name than 'anonymous'
1822 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1824 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1825 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1829 switch (LEVELCLASS(leveldir_current))
1831 case LEVELCLASS_TUTORIAL:
1832 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1835 case LEVELCLASS_CONTRIB:
1836 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1837 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1840 case LEVELCLASS_PRIVATE:
1841 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1842 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1846 // keep default value
1853 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1855 static boolean clipboard_elements_initialized = FALSE;
1858 InitElementPropertiesStatic();
1860 li = *level; // copy level data into temporary buffer
1861 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1862 *level = li; // copy temporary buffer back to level data
1864 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1867 struct ElementInfo *ei = &element_info[element];
1869 if (element == EL_MM_GRAY_BALL)
1871 struct LevelInfo_MM *level_mm = level->native_mm_level;
1874 for (j = 0; j < level->num_mm_ball_contents; j++)
1875 level->mm_ball_content[j] =
1876 map_element_MM_to_RND(level_mm->ball_content[j]);
1879 // never initialize clipboard elements after the very first time
1880 // (to be able to use clipboard elements between several levels)
1881 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1884 if (IS_ENVELOPE(element))
1886 int envelope_nr = element - EL_ENVELOPE_1;
1888 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1890 level->envelope[envelope_nr] = xx_envelope;
1893 if (IS_CUSTOM_ELEMENT(element) ||
1894 IS_GROUP_ELEMENT(element) ||
1895 IS_INTERNAL_ELEMENT(element))
1897 xx_ei = *ei; // copy element data into temporary buffer
1899 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1904 setElementChangePages(ei, 1);
1905 setElementChangeInfoToDefaults(ei->change);
1907 if (IS_CUSTOM_ELEMENT(element) ||
1908 IS_GROUP_ELEMENT(element))
1910 setElementDescriptionToDefault(ei);
1912 ei->modified_settings = FALSE;
1915 if (IS_CUSTOM_ELEMENT(element) ||
1916 IS_INTERNAL_ELEMENT(element))
1918 // internal values used in level editor
1920 ei->access_type = 0;
1921 ei->access_layer = 0;
1922 ei->access_protected = 0;
1923 ei->walk_to_action = 0;
1924 ei->smash_targets = 0;
1927 ei->can_explode_by_fire = FALSE;
1928 ei->can_explode_smashed = FALSE;
1929 ei->can_explode_impact = FALSE;
1931 ei->current_change_page = 0;
1934 if (IS_GROUP_ELEMENT(element) ||
1935 IS_INTERNAL_ELEMENT(element))
1937 struct ElementGroupInfo *group;
1939 // initialize memory for list of elements in group
1940 if (ei->group == NULL)
1941 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1945 xx_group = *group; // copy group data into temporary buffer
1947 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1952 if (IS_EMPTY_ELEMENT(element) ||
1953 IS_INTERNAL_ELEMENT(element))
1955 xx_ei = *ei; // copy element data into temporary buffer
1957 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1963 clipboard_elements_initialized = TRUE;
1966 static void setLevelInfoToDefaults(struct LevelInfo *level,
1967 boolean level_info_only,
1968 boolean reset_file_status)
1970 setLevelInfoToDefaults_Level(level);
1972 if (!level_info_only)
1973 setLevelInfoToDefaults_Elements(level);
1975 if (reset_file_status)
1977 level->no_valid_file = FALSE;
1978 level->no_level_file = FALSE;
1981 level->changed = FALSE;
1984 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1986 level_file_info->nr = 0;
1987 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1988 level_file_info->packed = FALSE;
1990 setString(&level_file_info->basename, NULL);
1991 setString(&level_file_info->filename, NULL);
1994 int getMappedElement_SB(int, boolean);
1996 static void ActivateLevelTemplate(void)
2000 if (check_special_flags("load_xsb_to_ces"))
2002 // fill smaller playfields with padding "beyond border wall" elements
2003 if (level.fieldx < level_template.fieldx ||
2004 level.fieldy < level_template.fieldy)
2006 short field[level.fieldx][level.fieldy];
2007 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2008 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2009 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2010 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2012 // copy old playfield (which is smaller than the visible area)
2013 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2014 field[x][y] = level.field[x][y];
2016 // fill new, larger playfield with "beyond border wall" elements
2017 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2018 level.field[x][y] = getMappedElement_SB('_', TRUE);
2020 // copy the old playfield to the middle of the new playfield
2021 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2022 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2024 level.fieldx = new_fieldx;
2025 level.fieldy = new_fieldy;
2029 // Currently there is no special action needed to activate the template
2030 // data, because 'element_info' property settings overwrite the original
2031 // level data, while all other variables do not change.
2033 // Exception: 'from_level_template' elements in the original level playfield
2034 // are overwritten with the corresponding elements at the same position in
2035 // playfield from the level template.
2037 for (x = 0; x < level.fieldx; x++)
2038 for (y = 0; y < level.fieldy; y++)
2039 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2040 level.field[x][y] = level_template.field[x][y];
2042 if (check_special_flags("load_xsb_to_ces"))
2044 struct LevelInfo level_backup = level;
2046 // overwrite all individual level settings from template level settings
2047 level = level_template;
2049 // restore level file info
2050 level.file_info = level_backup.file_info;
2052 // restore playfield size
2053 level.fieldx = level_backup.fieldx;
2054 level.fieldy = level_backup.fieldy;
2056 // restore playfield content
2057 for (x = 0; x < level.fieldx; x++)
2058 for (y = 0; y < level.fieldy; y++)
2059 level.field[x][y] = level_backup.field[x][y];
2061 // restore name and author from individual level
2062 strcpy(level.name, level_backup.name);
2063 strcpy(level.author, level_backup.author);
2065 // restore flag "use_custom_template"
2066 level.use_custom_template = level_backup.use_custom_template;
2070 static char *getLevelFilenameFromBasename(char *basename)
2072 static char *filename = NULL;
2074 checked_free(filename);
2076 filename = getPath2(getCurrentLevelDir(), basename);
2081 static int getFileTypeFromBasename(char *basename)
2083 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2085 static char *filename = NULL;
2086 struct stat file_status;
2088 // ---------- try to determine file type from filename ----------
2090 // check for typical filename of a Supaplex level package file
2091 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2092 return LEVEL_FILE_TYPE_SP;
2094 // check for typical filename of a Diamond Caves II level package file
2095 if (strSuffixLower(basename, ".dc") ||
2096 strSuffixLower(basename, ".dc2"))
2097 return LEVEL_FILE_TYPE_DC;
2099 // check for typical filename of a Sokoban level package file
2100 if (strSuffixLower(basename, ".xsb") &&
2101 strchr(basename, '%') == NULL)
2102 return LEVEL_FILE_TYPE_SB;
2104 // ---------- try to determine file type from filesize ----------
2106 checked_free(filename);
2107 filename = getPath2(getCurrentLevelDir(), basename);
2109 if (stat(filename, &file_status) == 0)
2111 // check for typical filesize of a Supaplex level package file
2112 if (file_status.st_size == 170496)
2113 return LEVEL_FILE_TYPE_SP;
2116 return LEVEL_FILE_TYPE_UNKNOWN;
2119 static int getFileTypeFromMagicBytes(char *filename, int type)
2123 if ((file = openFile(filename, MODE_READ)))
2125 char chunk_name[CHUNK_ID_LEN + 1];
2127 getFileChunkBE(file, chunk_name, NULL);
2129 if (strEqual(chunk_name, "MMII") ||
2130 strEqual(chunk_name, "MIRR"))
2131 type = LEVEL_FILE_TYPE_MM;
2139 static boolean checkForPackageFromBasename(char *basename)
2141 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2142 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2144 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2147 static char *getSingleLevelBasenameExt(int nr, char *extension)
2149 static char basename[MAX_FILENAME_LEN];
2152 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2154 sprintf(basename, "%03d.%s", nr, extension);
2159 static char *getSingleLevelBasename(int nr)
2161 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2164 static char *getPackedLevelBasename(int type)
2166 static char basename[MAX_FILENAME_LEN];
2167 char *directory = getCurrentLevelDir();
2169 DirectoryEntry *dir_entry;
2171 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2173 if ((dir = openDirectory(directory)) == NULL)
2175 Warn("cannot read current level directory '%s'", directory);
2180 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2182 char *entry_basename = dir_entry->basename;
2183 int entry_type = getFileTypeFromBasename(entry_basename);
2185 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2187 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2190 strcpy(basename, entry_basename);
2197 closeDirectory(dir);
2202 static char *getSingleLevelFilename(int nr)
2204 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2207 #if ENABLE_UNUSED_CODE
2208 static char *getPackedLevelFilename(int type)
2210 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2214 char *getDefaultLevelFilename(int nr)
2216 return getSingleLevelFilename(nr);
2219 #if ENABLE_UNUSED_CODE
2220 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2224 lfi->packed = FALSE;
2226 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2227 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2231 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2232 int type, char *format, ...)
2234 static char basename[MAX_FILENAME_LEN];
2237 va_start(ap, format);
2238 vsprintf(basename, format, ap);
2242 lfi->packed = FALSE;
2244 setString(&lfi->basename, basename);
2245 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2248 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2254 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2255 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2258 static int getFiletypeFromID(char *filetype_id)
2260 char *filetype_id_lower;
2261 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2264 if (filetype_id == NULL)
2265 return LEVEL_FILE_TYPE_UNKNOWN;
2267 filetype_id_lower = getStringToLower(filetype_id);
2269 for (i = 0; filetype_id_list[i].id != NULL; i++)
2271 char *id_lower = getStringToLower(filetype_id_list[i].id);
2273 if (strEqual(filetype_id_lower, id_lower))
2274 filetype = filetype_id_list[i].filetype;
2278 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2282 free(filetype_id_lower);
2287 char *getLocalLevelTemplateFilename(void)
2289 return getDefaultLevelFilename(-1);
2292 char *getGlobalLevelTemplateFilename(void)
2294 // global variable "leveldir_current" must be modified in the loop below
2295 LevelDirTree *leveldir_current_last = leveldir_current;
2296 char *filename = NULL;
2298 // check for template level in path from current to topmost tree node
2300 while (leveldir_current != NULL)
2302 filename = getDefaultLevelFilename(-1);
2304 if (fileExists(filename))
2307 leveldir_current = leveldir_current->node_parent;
2310 // restore global variable "leveldir_current" modified in above loop
2311 leveldir_current = leveldir_current_last;
2316 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2320 // special case: level number is negative => check for level template file
2323 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2324 getSingleLevelBasename(-1));
2326 // replace local level template filename with global template filename
2327 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2329 // no fallback if template file not existing
2333 // special case: check for file name/pattern specified in "levelinfo.conf"
2334 if (leveldir_current->level_filename != NULL)
2336 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2338 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2339 leveldir_current->level_filename, nr);
2341 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2343 if (fileExists(lfi->filename))
2346 else if (leveldir_current->level_filetype != NULL)
2348 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2350 // check for specified native level file with standard file name
2351 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2352 "%03d.%s", nr, LEVELFILE_EXTENSION);
2353 if (fileExists(lfi->filename))
2357 // check for native Rocks'n'Diamonds level file
2358 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2359 "%03d.%s", nr, LEVELFILE_EXTENSION);
2360 if (fileExists(lfi->filename))
2363 // check for Emerald Mine level file (V1)
2364 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2365 'a' + (nr / 10) % 26, '0' + nr % 10);
2366 if (fileExists(lfi->filename))
2368 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2369 'A' + (nr / 10) % 26, '0' + nr % 10);
2370 if (fileExists(lfi->filename))
2373 // check for Emerald Mine level file (V2 to V5)
2374 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2375 if (fileExists(lfi->filename))
2378 // check for Emerald Mine level file (V6 / single mode)
2379 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2380 if (fileExists(lfi->filename))
2382 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2383 if (fileExists(lfi->filename))
2386 // check for Emerald Mine level file (V6 / teamwork mode)
2387 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2388 if (fileExists(lfi->filename))
2390 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2391 if (fileExists(lfi->filename))
2394 // check for various packed level file formats
2395 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2396 if (fileExists(lfi->filename))
2399 // no known level file found -- use default values (and fail later)
2400 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2401 "%03d.%s", nr, LEVELFILE_EXTENSION);
2404 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2406 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2407 lfi->type = getFileTypeFromBasename(lfi->basename);
2409 if (lfi->type == LEVEL_FILE_TYPE_RND)
2410 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2413 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2415 // always start with reliable default values
2416 setFileInfoToDefaults(level_file_info);
2418 level_file_info->nr = nr; // set requested level number
2420 determineLevelFileInfo_Filename(level_file_info);
2421 determineLevelFileInfo_Filetype(level_file_info);
2424 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2425 struct LevelFileInfo *lfi_to)
2427 lfi_to->nr = lfi_from->nr;
2428 lfi_to->type = lfi_from->type;
2429 lfi_to->packed = lfi_from->packed;
2431 setString(&lfi_to->basename, lfi_from->basename);
2432 setString(&lfi_to->filename, lfi_from->filename);
2435 // ----------------------------------------------------------------------------
2436 // functions for loading R'n'D level
2437 // ----------------------------------------------------------------------------
2439 int getMappedElement(int element)
2441 // remap some (historic, now obsolete) elements
2445 case EL_PLAYER_OBSOLETE:
2446 element = EL_PLAYER_1;
2449 case EL_KEY_OBSOLETE:
2453 case EL_EM_KEY_1_FILE_OBSOLETE:
2454 element = EL_EM_KEY_1;
2457 case EL_EM_KEY_2_FILE_OBSOLETE:
2458 element = EL_EM_KEY_2;
2461 case EL_EM_KEY_3_FILE_OBSOLETE:
2462 element = EL_EM_KEY_3;
2465 case EL_EM_KEY_4_FILE_OBSOLETE:
2466 element = EL_EM_KEY_4;
2469 case EL_ENVELOPE_OBSOLETE:
2470 element = EL_ENVELOPE_1;
2478 if (element >= NUM_FILE_ELEMENTS)
2480 Warn("invalid level element %d", element);
2482 element = EL_UNKNOWN;
2490 static int getMappedElementByVersion(int element, int game_version)
2492 // remap some elements due to certain game version
2494 if (game_version <= VERSION_IDENT(2,2,0,0))
2496 // map game font elements
2497 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2498 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2499 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2500 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2503 if (game_version < VERSION_IDENT(3,0,0,0))
2505 // map Supaplex gravity tube elements
2506 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2507 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2508 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2509 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2516 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2518 level->file_version = getFileVersion(file);
2519 level->game_version = getFileVersion(file);
2524 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2526 level->creation_date.year = getFile16BitBE(file);
2527 level->creation_date.month = getFile8Bit(file);
2528 level->creation_date.day = getFile8Bit(file);
2530 level->creation_date.src = DATE_SRC_LEVELFILE;
2535 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2537 int initial_player_stepsize;
2538 int initial_player_gravity;
2541 level->fieldx = getFile8Bit(file);
2542 level->fieldy = getFile8Bit(file);
2544 level->time = getFile16BitBE(file);
2545 level->gems_needed = getFile16BitBE(file);
2547 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2548 level->name[i] = getFile8Bit(file);
2549 level->name[MAX_LEVEL_NAME_LEN] = 0;
2551 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2552 level->score[i] = getFile8Bit(file);
2554 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2555 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2556 for (y = 0; y < 3; y++)
2557 for (x = 0; x < 3; x++)
2558 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2560 level->amoeba_speed = getFile8Bit(file);
2561 level->time_magic_wall = getFile8Bit(file);
2562 level->time_wheel = getFile8Bit(file);
2563 level->amoeba_content = getMappedElement(getFile8Bit(file));
2565 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2568 for (i = 0; i < MAX_PLAYERS; i++)
2569 level->initial_player_stepsize[i] = initial_player_stepsize;
2571 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2573 for (i = 0; i < MAX_PLAYERS; i++)
2574 level->initial_player_gravity[i] = initial_player_gravity;
2576 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2577 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2579 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2581 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2582 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2583 level->can_move_into_acid_bits = getFile32BitBE(file);
2584 level->dont_collide_with_bits = getFile8Bit(file);
2586 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2587 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2589 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2590 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2591 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2593 level->game_engine_type = getFile8Bit(file);
2595 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2600 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2604 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2605 level->name[i] = getFile8Bit(file);
2606 level->name[MAX_LEVEL_NAME_LEN] = 0;
2611 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2615 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2616 level->author[i] = getFile8Bit(file);
2617 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2622 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2625 int chunk_size_expected = level->fieldx * level->fieldy;
2627 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2628 stored with 16-bit encoding (and should be twice as big then).
2629 Even worse, playfield data was stored 16-bit when only yamyam content
2630 contained 16-bit elements and vice versa. */
2632 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2633 chunk_size_expected *= 2;
2635 if (chunk_size_expected != chunk_size)
2637 ReadUnusedBytesFromFile(file, chunk_size);
2638 return chunk_size_expected;
2641 for (y = 0; y < level->fieldy; y++)
2642 for (x = 0; x < level->fieldx; x++)
2643 level->field[x][y] =
2644 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2649 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2652 int header_size = 4;
2653 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2654 int chunk_size_expected = header_size + content_size;
2656 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2657 stored with 16-bit encoding (and should be twice as big then).
2658 Even worse, playfield data was stored 16-bit when only yamyam content
2659 contained 16-bit elements and vice versa. */
2661 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2662 chunk_size_expected += content_size;
2664 if (chunk_size_expected != chunk_size)
2666 ReadUnusedBytesFromFile(file, chunk_size);
2667 return chunk_size_expected;
2671 level->num_yamyam_contents = getFile8Bit(file);
2675 // correct invalid number of content fields -- should never happen
2676 if (level->num_yamyam_contents < 1 ||
2677 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2678 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2680 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2681 for (y = 0; y < 3; y++)
2682 for (x = 0; x < 3; x++)
2683 level->yamyam_content[i].e[x][y] =
2684 getMappedElement(level->encoding_16bit_field ?
2685 getFile16BitBE(file) : getFile8Bit(file));
2689 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2694 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2696 element = getMappedElement(getFile16BitBE(file));
2697 num_contents = getFile8Bit(file);
2699 getFile8Bit(file); // content x size (unused)
2700 getFile8Bit(file); // content y size (unused)
2702 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2704 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2705 for (y = 0; y < 3; y++)
2706 for (x = 0; x < 3; x++)
2707 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2709 // correct invalid number of content fields -- should never happen
2710 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2711 num_contents = STD_ELEMENT_CONTENTS;
2713 if (element == EL_YAMYAM)
2715 level->num_yamyam_contents = num_contents;
2717 for (i = 0; i < num_contents; i++)
2718 for (y = 0; y < 3; y++)
2719 for (x = 0; x < 3; x++)
2720 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2722 else if (element == EL_BD_AMOEBA)
2724 level->amoeba_content = content_array[0][0][0];
2728 Warn("cannot load content for element '%d'", element);
2734 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2740 int chunk_size_expected;
2742 element = getMappedElement(getFile16BitBE(file));
2743 if (!IS_ENVELOPE(element))
2744 element = EL_ENVELOPE_1;
2746 envelope_nr = element - EL_ENVELOPE_1;
2748 envelope_len = getFile16BitBE(file);
2750 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2751 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2753 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2755 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2756 if (chunk_size_expected != chunk_size)
2758 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2759 return chunk_size_expected;
2762 for (i = 0; i < envelope_len; i++)
2763 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2768 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2770 int num_changed_custom_elements = getFile16BitBE(file);
2771 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2774 if (chunk_size_expected != chunk_size)
2776 ReadUnusedBytesFromFile(file, chunk_size - 2);
2777 return chunk_size_expected;
2780 for (i = 0; i < num_changed_custom_elements; i++)
2782 int element = getMappedElement(getFile16BitBE(file));
2783 int properties = getFile32BitBE(file);
2785 if (IS_CUSTOM_ELEMENT(element))
2786 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2788 Warn("invalid custom element number %d", element);
2790 // older game versions that wrote level files with CUS1 chunks used
2791 // different default push delay values (not yet stored in level file)
2792 element_info[element].push_delay_fixed = 2;
2793 element_info[element].push_delay_random = 8;
2796 level->file_has_custom_elements = TRUE;
2801 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2803 int num_changed_custom_elements = getFile16BitBE(file);
2804 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2807 if (chunk_size_expected != chunk_size)
2809 ReadUnusedBytesFromFile(file, chunk_size - 2);
2810 return chunk_size_expected;
2813 for (i = 0; i < num_changed_custom_elements; i++)
2815 int element = getMappedElement(getFile16BitBE(file));
2816 int custom_target_element = getMappedElement(getFile16BitBE(file));
2818 if (IS_CUSTOM_ELEMENT(element))
2819 element_info[element].change->target_element = custom_target_element;
2821 Warn("invalid custom element number %d", element);
2824 level->file_has_custom_elements = TRUE;
2829 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2831 int num_changed_custom_elements = getFile16BitBE(file);
2832 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2835 if (chunk_size_expected != chunk_size)
2837 ReadUnusedBytesFromFile(file, chunk_size - 2);
2838 return chunk_size_expected;
2841 for (i = 0; i < num_changed_custom_elements; i++)
2843 int element = getMappedElement(getFile16BitBE(file));
2844 struct ElementInfo *ei = &element_info[element];
2845 unsigned int event_bits;
2847 if (!IS_CUSTOM_ELEMENT(element))
2849 Warn("invalid custom element number %d", element);
2851 element = EL_INTERNAL_DUMMY;
2854 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2855 ei->description[j] = getFile8Bit(file);
2856 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2858 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2860 // some free bytes for future properties and padding
2861 ReadUnusedBytesFromFile(file, 7);
2863 ei->use_gfx_element = getFile8Bit(file);
2864 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2866 ei->collect_score_initial = getFile8Bit(file);
2867 ei->collect_count_initial = getFile8Bit(file);
2869 ei->push_delay_fixed = getFile16BitBE(file);
2870 ei->push_delay_random = getFile16BitBE(file);
2871 ei->move_delay_fixed = getFile16BitBE(file);
2872 ei->move_delay_random = getFile16BitBE(file);
2874 ei->move_pattern = getFile16BitBE(file);
2875 ei->move_direction_initial = getFile8Bit(file);
2876 ei->move_stepsize = getFile8Bit(file);
2878 for (y = 0; y < 3; y++)
2879 for (x = 0; x < 3; x++)
2880 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2882 // bits 0 - 31 of "has_event[]"
2883 event_bits = getFile32BitBE(file);
2884 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2885 if (event_bits & (1u << j))
2886 ei->change->has_event[j] = TRUE;
2888 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2890 ei->change->delay_fixed = getFile16BitBE(file);
2891 ei->change->delay_random = getFile16BitBE(file);
2892 ei->change->delay_frames = getFile16BitBE(file);
2894 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2896 ei->change->explode = getFile8Bit(file);
2897 ei->change->use_target_content = getFile8Bit(file);
2898 ei->change->only_if_complete = getFile8Bit(file);
2899 ei->change->use_random_replace = getFile8Bit(file);
2901 ei->change->random_percentage = getFile8Bit(file);
2902 ei->change->replace_when = getFile8Bit(file);
2904 for (y = 0; y < 3; y++)
2905 for (x = 0; x < 3; x++)
2906 ei->change->target_content.e[x][y] =
2907 getMappedElement(getFile16BitBE(file));
2909 ei->slippery_type = getFile8Bit(file);
2911 // some free bytes for future properties and padding
2912 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2914 // mark that this custom element has been modified
2915 ei->modified_settings = TRUE;
2918 level->file_has_custom_elements = TRUE;
2923 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2925 struct ElementInfo *ei;
2926 int chunk_size_expected;
2930 // ---------- custom element base property values (96 bytes) ----------------
2932 element = getMappedElement(getFile16BitBE(file));
2934 if (!IS_CUSTOM_ELEMENT(element))
2936 Warn("invalid custom element number %d", element);
2938 ReadUnusedBytesFromFile(file, chunk_size - 2);
2943 ei = &element_info[element];
2945 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2946 ei->description[i] = getFile8Bit(file);
2947 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2949 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2951 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2953 ei->num_change_pages = getFile8Bit(file);
2955 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2956 if (chunk_size_expected != chunk_size)
2958 ReadUnusedBytesFromFile(file, chunk_size - 43);
2959 return chunk_size_expected;
2962 ei->ce_value_fixed_initial = getFile16BitBE(file);
2963 ei->ce_value_random_initial = getFile16BitBE(file);
2964 ei->use_last_ce_value = getFile8Bit(file);
2966 ei->use_gfx_element = getFile8Bit(file);
2967 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2969 ei->collect_score_initial = getFile8Bit(file);
2970 ei->collect_count_initial = getFile8Bit(file);
2972 ei->drop_delay_fixed = getFile8Bit(file);
2973 ei->push_delay_fixed = getFile8Bit(file);
2974 ei->drop_delay_random = getFile8Bit(file);
2975 ei->push_delay_random = getFile8Bit(file);
2976 ei->move_delay_fixed = getFile16BitBE(file);
2977 ei->move_delay_random = getFile16BitBE(file);
2979 // bits 0 - 15 of "move_pattern" ...
2980 ei->move_pattern = getFile16BitBE(file);
2981 ei->move_direction_initial = getFile8Bit(file);
2982 ei->move_stepsize = getFile8Bit(file);
2984 ei->slippery_type = getFile8Bit(file);
2986 for (y = 0; y < 3; y++)
2987 for (x = 0; x < 3; x++)
2988 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2990 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2991 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2992 ei->move_leave_type = getFile8Bit(file);
2994 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2995 ei->move_pattern |= (getFile16BitBE(file) << 16);
2997 ei->access_direction = getFile8Bit(file);
2999 ei->explosion_delay = getFile8Bit(file);
3000 ei->ignition_delay = getFile8Bit(file);
3001 ei->explosion_type = getFile8Bit(file);
3003 // some free bytes for future custom property values and padding
3004 ReadUnusedBytesFromFile(file, 1);
3006 // ---------- change page property values (48 bytes) ------------------------
3008 setElementChangePages(ei, ei->num_change_pages);
3010 for (i = 0; i < ei->num_change_pages; i++)
3012 struct ElementChangeInfo *change = &ei->change_page[i];
3013 unsigned int event_bits;
3015 // always start with reliable default values
3016 setElementChangeInfoToDefaults(change);
3018 // bits 0 - 31 of "has_event[]" ...
3019 event_bits = getFile32BitBE(file);
3020 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3021 if (event_bits & (1u << j))
3022 change->has_event[j] = TRUE;
3024 change->target_element = getMappedElement(getFile16BitBE(file));
3026 change->delay_fixed = getFile16BitBE(file);
3027 change->delay_random = getFile16BitBE(file);
3028 change->delay_frames = getFile16BitBE(file);
3030 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3032 change->explode = getFile8Bit(file);
3033 change->use_target_content = getFile8Bit(file);
3034 change->only_if_complete = getFile8Bit(file);
3035 change->use_random_replace = getFile8Bit(file);
3037 change->random_percentage = getFile8Bit(file);
3038 change->replace_when = getFile8Bit(file);
3040 for (y = 0; y < 3; y++)
3041 for (x = 0; x < 3; x++)
3042 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3044 change->can_change = getFile8Bit(file);
3046 change->trigger_side = getFile8Bit(file);
3048 change->trigger_player = getFile8Bit(file);
3049 change->trigger_page = getFile8Bit(file);
3051 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3052 CH_PAGE_ANY : (1 << change->trigger_page));
3054 change->has_action = getFile8Bit(file);
3055 change->action_type = getFile8Bit(file);
3056 change->action_mode = getFile8Bit(file);
3057 change->action_arg = getFile16BitBE(file);
3059 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3060 event_bits = getFile8Bit(file);
3061 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3062 if (event_bits & (1u << (j - 32)))
3063 change->has_event[j] = TRUE;
3066 // mark this custom element as modified
3067 ei->modified_settings = TRUE;
3069 level->file_has_custom_elements = TRUE;
3074 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3076 struct ElementInfo *ei;
3077 struct ElementGroupInfo *group;
3081 element = getMappedElement(getFile16BitBE(file));
3083 if (!IS_GROUP_ELEMENT(element))
3085 Warn("invalid group element number %d", element);
3087 ReadUnusedBytesFromFile(file, chunk_size - 2);
3092 ei = &element_info[element];
3094 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3095 ei->description[i] = getFile8Bit(file);
3096 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3098 group = element_info[element].group;
3100 group->num_elements = getFile8Bit(file);
3102 ei->use_gfx_element = getFile8Bit(file);
3103 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3105 group->choice_mode = getFile8Bit(file);
3107 // some free bytes for future values and padding
3108 ReadUnusedBytesFromFile(file, 3);
3110 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3111 group->element[i] = getMappedElement(getFile16BitBE(file));
3113 // mark this group element as modified
3114 element_info[element].modified_settings = TRUE;
3116 level->file_has_custom_elements = TRUE;
3121 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3122 int element, int real_element)
3124 int micro_chunk_size = 0;
3125 int conf_type = getFile8Bit(file);
3126 int byte_mask = conf_type & CONF_MASK_BYTES;
3127 boolean element_found = FALSE;
3130 micro_chunk_size += 1;
3132 if (byte_mask == CONF_MASK_MULTI_BYTES)
3134 int num_bytes = getFile16BitBE(file);
3135 byte *buffer = checked_malloc(num_bytes);
3137 ReadBytesFromFile(file, buffer, num_bytes);
3139 for (i = 0; conf[i].data_type != -1; i++)
3141 if (conf[i].element == element &&
3142 conf[i].conf_type == conf_type)
3144 int data_type = conf[i].data_type;
3145 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3146 int max_num_entities = conf[i].max_num_entities;
3148 if (num_entities > max_num_entities)
3150 Warn("truncating number of entities for element %d from %d to %d",
3151 element, num_entities, max_num_entities);
3153 num_entities = max_num_entities;
3156 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3157 data_type == TYPE_CONTENT_LIST))
3159 // for element and content lists, zero entities are not allowed
3160 Warn("found empty list of entities for element %d", element);
3162 // do not set "num_entities" here to prevent reading behind buffer
3164 *(int *)(conf[i].num_entities) = 1; // at least one is required
3168 *(int *)(conf[i].num_entities) = num_entities;
3171 element_found = TRUE;
3173 if (data_type == TYPE_STRING)
3175 char *string = (char *)(conf[i].value);
3178 for (j = 0; j < max_num_entities; j++)
3179 string[j] = (j < num_entities ? buffer[j] : '\0');
3181 else if (data_type == TYPE_ELEMENT_LIST)
3183 int *element_array = (int *)(conf[i].value);
3186 for (j = 0; j < num_entities; j++)
3188 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3190 else if (data_type == TYPE_CONTENT_LIST)
3192 struct Content *content= (struct Content *)(conf[i].value);
3195 for (c = 0; c < num_entities; c++)
3196 for (y = 0; y < 3; y++)
3197 for (x = 0; x < 3; x++)
3198 content[c].e[x][y] =
3199 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3202 element_found = FALSE;
3208 checked_free(buffer);
3210 micro_chunk_size += 2 + num_bytes;
3212 else // constant size configuration data (1, 2 or 4 bytes)
3214 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3215 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3216 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3218 for (i = 0; conf[i].data_type != -1; i++)
3220 if (conf[i].element == element &&
3221 conf[i].conf_type == conf_type)
3223 int data_type = conf[i].data_type;
3225 if (data_type == TYPE_ELEMENT)
3226 value = getMappedElement(value);
3228 if (data_type == TYPE_BOOLEAN)
3229 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3231 *(int *) (conf[i].value) = value;
3233 element_found = TRUE;
3239 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3244 char *error_conf_chunk_bytes =
3245 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3246 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3247 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3248 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3249 int error_element = real_element;
3251 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3252 error_conf_chunk_bytes, error_conf_chunk_token,
3253 error_element, EL_NAME(error_element));
3256 return micro_chunk_size;
3259 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3261 int real_chunk_size = 0;
3263 li = *level; // copy level data into temporary buffer
3265 while (!checkEndOfFile(file))
3267 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3269 if (real_chunk_size >= chunk_size)
3273 *level = li; // copy temporary buffer back to level data
3275 return real_chunk_size;
3278 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3280 int real_chunk_size = 0;
3282 li = *level; // copy level data into temporary buffer
3284 while (!checkEndOfFile(file))
3286 int element = getMappedElement(getFile16BitBE(file));
3288 real_chunk_size += 2;
3289 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3291 if (real_chunk_size >= chunk_size)
3295 *level = li; // copy temporary buffer back to level data
3297 return real_chunk_size;
3300 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3302 int real_chunk_size = 0;
3304 li = *level; // copy level data into temporary buffer
3306 while (!checkEndOfFile(file))
3308 int element = getMappedElement(getFile16BitBE(file));
3310 real_chunk_size += 2;
3311 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3313 if (real_chunk_size >= chunk_size)
3317 *level = li; // copy temporary buffer back to level data
3319 return real_chunk_size;
3322 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3324 int element = getMappedElement(getFile16BitBE(file));
3325 int envelope_nr = element - EL_ENVELOPE_1;
3326 int real_chunk_size = 2;
3328 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3330 while (!checkEndOfFile(file))
3332 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3335 if (real_chunk_size >= chunk_size)
3339 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3341 return real_chunk_size;
3344 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3346 int element = getMappedElement(getFile16BitBE(file));
3347 int real_chunk_size = 2;
3348 struct ElementInfo *ei = &element_info[element];
3351 xx_ei = *ei; // copy element data into temporary buffer
3353 xx_ei.num_change_pages = -1;
3355 while (!checkEndOfFile(file))
3357 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3359 if (xx_ei.num_change_pages != -1)
3362 if (real_chunk_size >= chunk_size)
3368 if (ei->num_change_pages == -1)
3370 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3373 ei->num_change_pages = 1;
3375 setElementChangePages(ei, 1);
3376 setElementChangeInfoToDefaults(ei->change);
3378 return real_chunk_size;
3381 // initialize number of change pages stored for this custom element
3382 setElementChangePages(ei, ei->num_change_pages);
3383 for (i = 0; i < ei->num_change_pages; i++)
3384 setElementChangeInfoToDefaults(&ei->change_page[i]);
3386 // start with reading properties for the first change page
3387 xx_current_change_page = 0;
3389 while (!checkEndOfFile(file))
3391 // level file might contain invalid change page number
3392 if (xx_current_change_page >= ei->num_change_pages)
3395 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3397 xx_change = *change; // copy change data into temporary buffer
3399 resetEventBits(); // reset bits; change page might have changed
3401 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3404 *change = xx_change;
3406 setEventFlagsFromEventBits(change);
3408 if (real_chunk_size >= chunk_size)
3412 level->file_has_custom_elements = TRUE;
3414 return real_chunk_size;
3417 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3419 int element = getMappedElement(getFile16BitBE(file));
3420 int real_chunk_size = 2;
3421 struct ElementInfo *ei = &element_info[element];
3422 struct ElementGroupInfo *group = ei->group;
3427 xx_ei = *ei; // copy element data into temporary buffer
3428 xx_group = *group; // copy group data into temporary buffer
3430 while (!checkEndOfFile(file))
3432 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3435 if (real_chunk_size >= chunk_size)
3442 level->file_has_custom_elements = TRUE;
3444 return real_chunk_size;
3447 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3449 int element = getMappedElement(getFile16BitBE(file));
3450 int real_chunk_size = 2;
3451 struct ElementInfo *ei = &element_info[element];
3453 xx_ei = *ei; // copy element data into temporary buffer
3455 while (!checkEndOfFile(file))
3457 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3460 if (real_chunk_size >= chunk_size)
3466 level->file_has_custom_elements = TRUE;
3468 return real_chunk_size;
3471 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3472 struct LevelFileInfo *level_file_info,
3473 boolean level_info_only)
3475 char *filename = level_file_info->filename;
3476 char cookie[MAX_LINE_LEN];
3477 char chunk_name[CHUNK_ID_LEN + 1];
3481 if (!(file = openFile(filename, MODE_READ)))
3483 level->no_valid_file = TRUE;
3484 level->no_level_file = TRUE;
3486 if (level_info_only)
3489 Warn("cannot read level '%s' -- using empty level", filename);
3491 if (!setup.editor.use_template_for_new_levels)
3494 // if level file not found, try to initialize level data from template
3495 filename = getGlobalLevelTemplateFilename();
3497 if (!(file = openFile(filename, MODE_READ)))
3500 // default: for empty levels, use level template for custom elements
3501 level->use_custom_template = TRUE;
3503 level->no_valid_file = FALSE;
3506 getFileChunkBE(file, chunk_name, NULL);
3507 if (strEqual(chunk_name, "RND1"))
3509 getFile32BitBE(file); // not used
3511 getFileChunkBE(file, chunk_name, NULL);
3512 if (!strEqual(chunk_name, "CAVE"))
3514 level->no_valid_file = TRUE;
3516 Warn("unknown format of level file '%s'", filename);
3523 else // check for pre-2.0 file format with cookie string
3525 strcpy(cookie, chunk_name);
3526 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3528 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3529 cookie[strlen(cookie) - 1] = '\0';
3531 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3533 level->no_valid_file = TRUE;
3535 Warn("unknown format of level file '%s'", filename);
3542 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3544 level->no_valid_file = TRUE;
3546 Warn("unsupported version of level file '%s'", filename);
3553 // pre-2.0 level files have no game version, so use file version here
3554 level->game_version = level->file_version;
3557 if (level->file_version < FILE_VERSION_1_2)
3559 // level files from versions before 1.2.0 without chunk structure
3560 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3561 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3569 int (*loader)(File *, int, struct LevelInfo *);
3573 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3574 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3575 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3576 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3577 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3578 { "INFO", -1, LoadLevel_INFO },
3579 { "BODY", -1, LoadLevel_BODY },
3580 { "CONT", -1, LoadLevel_CONT },
3581 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3582 { "CNT3", -1, LoadLevel_CNT3 },
3583 { "CUS1", -1, LoadLevel_CUS1 },
3584 { "CUS2", -1, LoadLevel_CUS2 },
3585 { "CUS3", -1, LoadLevel_CUS3 },
3586 { "CUS4", -1, LoadLevel_CUS4 },
3587 { "GRP1", -1, LoadLevel_GRP1 },
3588 { "CONF", -1, LoadLevel_CONF },
3589 { "ELEM", -1, LoadLevel_ELEM },
3590 { "NOTE", -1, LoadLevel_NOTE },
3591 { "CUSX", -1, LoadLevel_CUSX },
3592 { "GRPX", -1, LoadLevel_GRPX },
3593 { "EMPX", -1, LoadLevel_EMPX },
3598 while (getFileChunkBE(file, chunk_name, &chunk_size))
3602 while (chunk_info[i].name != NULL &&
3603 !strEqual(chunk_name, chunk_info[i].name))
3606 if (chunk_info[i].name == NULL)
3608 Warn("unknown chunk '%s' in level file '%s'",
3609 chunk_name, filename);
3611 ReadUnusedBytesFromFile(file, chunk_size);
3613 else if (chunk_info[i].size != -1 &&
3614 chunk_info[i].size != chunk_size)
3616 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3617 chunk_size, chunk_name, filename);
3619 ReadUnusedBytesFromFile(file, chunk_size);
3623 // call function to load this level chunk
3624 int chunk_size_expected =
3625 (chunk_info[i].loader)(file, chunk_size, level);
3627 if (chunk_size_expected < 0)
3629 Warn("error reading chunk '%s' in level file '%s'",
3630 chunk_name, filename);
3635 // the size of some chunks cannot be checked before reading other
3636 // chunks first (like "HEAD" and "BODY") that contain some header
3637 // information, so check them here
3638 if (chunk_size_expected != chunk_size)
3640 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3641 chunk_size, chunk_name, filename);
3653 // ----------------------------------------------------------------------------
3654 // functions for loading BD level
3655 // ----------------------------------------------------------------------------
3657 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3659 struct LevelInfo_BD *level_bd = level->native_bd_level;
3661 level_bd->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3662 level_bd->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3665 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3667 struct LevelInfo_BD *level_bd = level->native_bd_level;
3669 level->fieldx = MIN(level_bd->width, MAX_LEV_FIELDX);
3670 level->fieldy = MIN(level_bd->height, MAX_LEV_FIELDY);
3674 // ----------------------------------------------------------------------------
3675 // functions for loading EM level
3676 // ----------------------------------------------------------------------------
3678 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3680 static int ball_xy[8][2] =
3691 struct LevelInfo_EM *level_em = level->native_em_level;
3692 struct CAVE *cav = level_em->cav;
3695 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3696 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3698 cav->time_seconds = level->time;
3699 cav->gems_needed = level->gems_needed;
3701 cav->emerald_score = level->score[SC_EMERALD];
3702 cav->diamond_score = level->score[SC_DIAMOND];
3703 cav->alien_score = level->score[SC_ROBOT];
3704 cav->tank_score = level->score[SC_SPACESHIP];
3705 cav->bug_score = level->score[SC_BUG];
3706 cav->eater_score = level->score[SC_YAMYAM];
3707 cav->nut_score = level->score[SC_NUT];
3708 cav->dynamite_score = level->score[SC_DYNAMITE];
3709 cav->key_score = level->score[SC_KEY];
3710 cav->exit_score = level->score[SC_TIME_BONUS];
3712 cav->num_eater_arrays = level->num_yamyam_contents;
3714 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3715 for (y = 0; y < 3; y++)
3716 for (x = 0; x < 3; x++)
3717 cav->eater_array[i][y * 3 + x] =
3718 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3720 cav->amoeba_time = level->amoeba_speed;
3721 cav->wonderwall_time = level->time_magic_wall;
3722 cav->wheel_time = level->time_wheel;
3724 cav->android_move_time = level->android_move_time;
3725 cav->android_clone_time = level->android_clone_time;
3726 cav->ball_random = level->ball_random;
3727 cav->ball_active = level->ball_active_initial;
3728 cav->ball_time = level->ball_time;
3729 cav->num_ball_arrays = level->num_ball_contents;
3731 cav->lenses_score = level->lenses_score;
3732 cav->magnify_score = level->magnify_score;
3733 cav->slurp_score = level->slurp_score;
3735 cav->lenses_time = level->lenses_time;
3736 cav->magnify_time = level->magnify_time;
3738 cav->wind_time = 9999;
3739 cav->wind_direction =
3740 map_direction_RND_to_EM(level->wind_direction_initial);
3742 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3743 for (j = 0; j < 8; j++)
3744 cav->ball_array[i][j] =
3745 map_element_RND_to_EM_cave(level->ball_content[i].
3746 e[ball_xy[j][0]][ball_xy[j][1]]);
3748 map_android_clone_elements_RND_to_EM(level);
3750 // first fill the complete playfield with the empty space element
3751 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3752 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3753 cav->cave[x][y] = Cblank;
3755 // then copy the real level contents from level file into the playfield
3756 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3758 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3760 if (level->field[x][y] == EL_AMOEBA_DEAD)
3761 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3763 cav->cave[x][y] = new_element;
3766 for (i = 0; i < MAX_PLAYERS; i++)
3768 cav->player_x[i] = -1;
3769 cav->player_y[i] = -1;
3772 // initialize player positions and delete players from the playfield
3773 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3775 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3777 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3779 cav->player_x[player_nr] = x;
3780 cav->player_y[player_nr] = y;
3782 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3787 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3789 static int ball_xy[8][2] =
3800 struct LevelInfo_EM *level_em = level->native_em_level;
3801 struct CAVE *cav = level_em->cav;
3804 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3805 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3807 level->time = cav->time_seconds;
3808 level->gems_needed = cav->gems_needed;
3810 sprintf(level->name, "Level %d", level->file_info.nr);
3812 level->score[SC_EMERALD] = cav->emerald_score;
3813 level->score[SC_DIAMOND] = cav->diamond_score;
3814 level->score[SC_ROBOT] = cav->alien_score;
3815 level->score[SC_SPACESHIP] = cav->tank_score;
3816 level->score[SC_BUG] = cav->bug_score;
3817 level->score[SC_YAMYAM] = cav->eater_score;
3818 level->score[SC_NUT] = cav->nut_score;
3819 level->score[SC_DYNAMITE] = cav->dynamite_score;
3820 level->score[SC_KEY] = cav->key_score;
3821 level->score[SC_TIME_BONUS] = cav->exit_score;
3823 level->num_yamyam_contents = cav->num_eater_arrays;
3825 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3826 for (y = 0; y < 3; y++)
3827 for (x = 0; x < 3; x++)
3828 level->yamyam_content[i].e[x][y] =
3829 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3831 level->amoeba_speed = cav->amoeba_time;
3832 level->time_magic_wall = cav->wonderwall_time;
3833 level->time_wheel = cav->wheel_time;
3835 level->android_move_time = cav->android_move_time;
3836 level->android_clone_time = cav->android_clone_time;
3837 level->ball_random = cav->ball_random;
3838 level->ball_active_initial = cav->ball_active;
3839 level->ball_time = cav->ball_time;
3840 level->num_ball_contents = cav->num_ball_arrays;
3842 level->lenses_score = cav->lenses_score;
3843 level->magnify_score = cav->magnify_score;
3844 level->slurp_score = cav->slurp_score;
3846 level->lenses_time = cav->lenses_time;
3847 level->magnify_time = cav->magnify_time;
3849 level->wind_direction_initial =
3850 map_direction_EM_to_RND(cav->wind_direction);
3852 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3853 for (j = 0; j < 8; j++)
3854 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3855 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3857 map_android_clone_elements_EM_to_RND(level);
3859 // convert the playfield (some elements need special treatment)
3860 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3862 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3864 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3865 new_element = EL_AMOEBA_DEAD;
3867 level->field[x][y] = new_element;
3870 for (i = 0; i < MAX_PLAYERS; i++)
3872 // in case of all players set to the same field, use the first player
3873 int nr = MAX_PLAYERS - i - 1;
3874 int jx = cav->player_x[nr];
3875 int jy = cav->player_y[nr];
3877 if (jx != -1 && jy != -1)
3878 level->field[jx][jy] = EL_PLAYER_1 + nr;
3881 // time score is counted for each 10 seconds left in Emerald Mine levels
3882 level->time_score_base = 10;
3886 // ----------------------------------------------------------------------------
3887 // functions for loading SP level
3888 // ----------------------------------------------------------------------------
3890 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3892 struct LevelInfo_SP *level_sp = level->native_sp_level;
3893 LevelInfoType *header = &level_sp->header;
3896 level_sp->width = level->fieldx;
3897 level_sp->height = level->fieldy;
3899 for (x = 0; x < level->fieldx; x++)
3900 for (y = 0; y < level->fieldy; y++)
3901 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3903 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3905 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3906 header->LevelTitle[i] = level->name[i];
3907 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3909 header->InfotronsNeeded = level->gems_needed;
3911 header->SpecialPortCount = 0;
3913 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3915 boolean gravity_port_found = FALSE;
3916 boolean gravity_port_valid = FALSE;
3917 int gravity_port_flag;
3918 int gravity_port_base_element;
3919 int element = level->field[x][y];
3921 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3922 element <= EL_SP_GRAVITY_ON_PORT_UP)
3924 gravity_port_found = TRUE;
3925 gravity_port_valid = TRUE;
3926 gravity_port_flag = 1;
3927 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3929 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3930 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3932 gravity_port_found = TRUE;
3933 gravity_port_valid = TRUE;
3934 gravity_port_flag = 0;
3935 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3937 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3938 element <= EL_SP_GRAVITY_PORT_UP)
3940 // change R'n'D style gravity inverting special port to normal port
3941 // (there are no gravity inverting ports in native Supaplex engine)
3943 gravity_port_found = TRUE;
3944 gravity_port_valid = FALSE;
3945 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3948 if (gravity_port_found)
3950 if (gravity_port_valid &&
3951 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3953 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3955 port->PortLocation = (y * level->fieldx + x) * 2;
3956 port->Gravity = gravity_port_flag;
3958 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3960 header->SpecialPortCount++;
3964 // change special gravity port to normal port
3966 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3969 level_sp->playfield[x][y] = element - EL_SP_START;
3974 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3976 struct LevelInfo_SP *level_sp = level->native_sp_level;
3977 LevelInfoType *header = &level_sp->header;
3978 boolean num_invalid_elements = 0;
3981 level->fieldx = level_sp->width;
3982 level->fieldy = level_sp->height;
3984 for (x = 0; x < level->fieldx; x++)
3986 for (y = 0; y < level->fieldy; y++)
3988 int element_old = level_sp->playfield[x][y];
3989 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3991 if (element_new == EL_UNKNOWN)
3993 num_invalid_elements++;
3995 Debug("level:native:SP", "invalid element %d at position %d, %d",
3999 level->field[x][y] = element_new;
4003 if (num_invalid_elements > 0)
4004 Warn("found %d invalid elements%s", num_invalid_elements,
4005 (!options.debug ? " (use '--debug' for more details)" : ""));
4007 for (i = 0; i < MAX_PLAYERS; i++)
4008 level->initial_player_gravity[i] =
4009 (header->InitialGravity == 1 ? TRUE : FALSE);
4011 // skip leading spaces
4012 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4013 if (header->LevelTitle[i] != ' ')
4017 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4018 level->name[j] = header->LevelTitle[i];
4019 level->name[j] = '\0';
4021 // cut trailing spaces
4023 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4024 level->name[j - 1] = '\0';
4026 level->gems_needed = header->InfotronsNeeded;
4028 for (i = 0; i < header->SpecialPortCount; i++)
4030 SpecialPortType *port = &header->SpecialPort[i];
4031 int port_location = port->PortLocation;
4032 int gravity = port->Gravity;
4033 int port_x, port_y, port_element;
4035 port_x = (port_location / 2) % level->fieldx;
4036 port_y = (port_location / 2) / level->fieldx;
4038 if (port_x < 0 || port_x >= level->fieldx ||
4039 port_y < 0 || port_y >= level->fieldy)
4041 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4046 port_element = level->field[port_x][port_y];
4048 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4049 port_element > EL_SP_GRAVITY_PORT_UP)
4051 Warn("no special port at position (%d, %d)", port_x, port_y);
4056 // change previous (wrong) gravity inverting special port to either
4057 // gravity enabling special port or gravity disabling special port
4058 level->field[port_x][port_y] +=
4059 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4060 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4063 // change special gravity ports without database entries to normal ports
4064 for (x = 0; x < level->fieldx; x++)
4065 for (y = 0; y < level->fieldy; y++)
4066 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4067 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4068 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4070 level->time = 0; // no time limit
4071 level->amoeba_speed = 0;
4072 level->time_magic_wall = 0;
4073 level->time_wheel = 0;
4074 level->amoeba_content = EL_EMPTY;
4076 // original Supaplex does not use score values -- rate by playing time
4077 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4078 level->score[i] = 0;
4080 level->rate_time_over_score = TRUE;
4082 // there are no yamyams in supaplex levels
4083 for (i = 0; i < level->num_yamyam_contents; i++)
4084 for (x = 0; x < 3; x++)
4085 for (y = 0; y < 3; y++)
4086 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4089 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4091 struct LevelInfo_SP *level_sp = level->native_sp_level;
4092 struct DemoInfo_SP *demo = &level_sp->demo;
4095 // always start with reliable default values
4096 demo->is_available = FALSE;
4099 if (TAPE_IS_EMPTY(tape))
4102 demo->level_nr = tape.level_nr; // (currently not used)
4104 level_sp->header.DemoRandomSeed = tape.random_seed;
4108 for (i = 0; i < tape.length; i++)
4110 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4111 int demo_repeat = tape.pos[i].delay;
4112 int demo_entries = (demo_repeat + 15) / 16;
4114 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4116 Warn("tape truncated: size exceeds maximum SP demo size %d",
4122 for (j = 0; j < demo_repeat / 16; j++)
4123 demo->data[demo->length++] = 0xf0 | demo_action;
4125 if (demo_repeat % 16)
4126 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4129 demo->is_available = TRUE;
4132 static void setTapeInfoToDefaults(void);
4134 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4136 struct LevelInfo_SP *level_sp = level->native_sp_level;
4137 struct DemoInfo_SP *demo = &level_sp->demo;
4138 char *filename = level->file_info.filename;
4141 // always start with reliable default values
4142 setTapeInfoToDefaults();
4144 if (!demo->is_available)
4147 tape.level_nr = demo->level_nr; // (currently not used)
4148 tape.random_seed = level_sp->header.DemoRandomSeed;
4150 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4153 tape.pos[tape.counter].delay = 0;
4155 for (i = 0; i < demo->length; i++)
4157 int demo_action = demo->data[i] & 0x0f;
4158 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4159 int tape_action = map_key_SP_to_RND(demo_action);
4160 int tape_repeat = demo_repeat + 1;
4161 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4162 boolean success = 0;
4165 for (j = 0; j < tape_repeat; j++)
4166 success = TapeAddAction(action);
4170 Warn("SP demo truncated: size exceeds maximum tape size %d",
4177 TapeHaltRecording();
4181 // ----------------------------------------------------------------------------
4182 // functions for loading MM level
4183 // ----------------------------------------------------------------------------
4185 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4187 struct LevelInfo_MM *level_mm = level->native_mm_level;
4190 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4191 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4193 level_mm->time = level->time;
4194 level_mm->kettles_needed = level->gems_needed;
4195 level_mm->auto_count_kettles = level->auto_count_gems;
4197 level_mm->mm_laser_red = level->mm_laser_red;
4198 level_mm->mm_laser_green = level->mm_laser_green;
4199 level_mm->mm_laser_blue = level->mm_laser_blue;
4201 level_mm->df_laser_red = level->df_laser_red;
4202 level_mm->df_laser_green = level->df_laser_green;
4203 level_mm->df_laser_blue = level->df_laser_blue;
4205 strcpy(level_mm->name, level->name);
4206 strcpy(level_mm->author, level->author);
4208 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4209 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4210 level_mm->score[SC_KEY] = level->score[SC_KEY];
4211 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4212 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4214 level_mm->amoeba_speed = level->amoeba_speed;
4215 level_mm->time_fuse = level->mm_time_fuse;
4216 level_mm->time_bomb = level->mm_time_bomb;
4217 level_mm->time_ball = level->mm_time_ball;
4218 level_mm->time_block = level->mm_time_block;
4220 level_mm->num_ball_contents = level->num_mm_ball_contents;
4221 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4222 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4223 level_mm->explode_ball = level->explode_mm_ball;
4225 for (i = 0; i < level->num_mm_ball_contents; i++)
4226 level_mm->ball_content[i] =
4227 map_element_RND_to_MM(level->mm_ball_content[i]);
4229 for (x = 0; x < level->fieldx; x++)
4230 for (y = 0; y < level->fieldy; y++)
4232 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4235 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4237 struct LevelInfo_MM *level_mm = level->native_mm_level;
4240 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4241 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4243 level->time = level_mm->time;
4244 level->gems_needed = level_mm->kettles_needed;
4245 level->auto_count_gems = level_mm->auto_count_kettles;
4247 level->mm_laser_red = level_mm->mm_laser_red;
4248 level->mm_laser_green = level_mm->mm_laser_green;
4249 level->mm_laser_blue = level_mm->mm_laser_blue;
4251 level->df_laser_red = level_mm->df_laser_red;
4252 level->df_laser_green = level_mm->df_laser_green;
4253 level->df_laser_blue = level_mm->df_laser_blue;
4255 strcpy(level->name, level_mm->name);
4257 // only overwrite author from 'levelinfo.conf' if author defined in level
4258 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4259 strcpy(level->author, level_mm->author);
4261 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4262 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4263 level->score[SC_KEY] = level_mm->score[SC_KEY];
4264 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4265 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4267 level->amoeba_speed = level_mm->amoeba_speed;
4268 level->mm_time_fuse = level_mm->time_fuse;
4269 level->mm_time_bomb = level_mm->time_bomb;
4270 level->mm_time_ball = level_mm->time_ball;
4271 level->mm_time_block = level_mm->time_block;
4273 level->num_mm_ball_contents = level_mm->num_ball_contents;
4274 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4275 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4276 level->explode_mm_ball = level_mm->explode_ball;
4278 for (i = 0; i < level->num_mm_ball_contents; i++)
4279 level->mm_ball_content[i] =
4280 map_element_MM_to_RND(level_mm->ball_content[i]);
4282 for (x = 0; x < level->fieldx; x++)
4283 for (y = 0; y < level->fieldy; y++)
4284 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4288 // ----------------------------------------------------------------------------
4289 // functions for loading DC level
4290 // ----------------------------------------------------------------------------
4292 #define DC_LEVEL_HEADER_SIZE 344
4294 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4297 static int last_data_encoded;
4301 int diff_hi, diff_lo;
4302 int data_hi, data_lo;
4303 unsigned short data_decoded;
4307 last_data_encoded = 0;
4314 diff = data_encoded - last_data_encoded;
4315 diff_hi = diff & ~0xff;
4316 diff_lo = diff & 0xff;
4320 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4321 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4322 data_hi = data_hi & 0xff00;
4324 data_decoded = data_hi | data_lo;
4326 last_data_encoded = data_encoded;
4328 offset1 = (offset1 + 1) % 31;
4329 offset2 = offset2 & 0xff;
4331 return data_decoded;
4334 static int getMappedElement_DC(int element)
4342 // 0x0117 - 0x036e: (?)
4345 // 0x042d - 0x0684: (?)
4361 element = EL_CRYSTAL;
4364 case 0x0e77: // quicksand (boulder)
4365 element = EL_QUICKSAND_FAST_FULL;
4368 case 0x0e99: // slow quicksand (boulder)
4369 element = EL_QUICKSAND_FULL;
4373 element = EL_EM_EXIT_OPEN;
4377 element = EL_EM_EXIT_CLOSED;
4381 element = EL_EM_STEEL_EXIT_OPEN;
4385 element = EL_EM_STEEL_EXIT_CLOSED;
4388 case 0x0f4f: // dynamite (lit 1)
4389 element = EL_EM_DYNAMITE_ACTIVE;
4392 case 0x0f57: // dynamite (lit 2)
4393 element = EL_EM_DYNAMITE_ACTIVE;
4396 case 0x0f5f: // dynamite (lit 3)
4397 element = EL_EM_DYNAMITE_ACTIVE;
4400 case 0x0f67: // dynamite (lit 4)
4401 element = EL_EM_DYNAMITE_ACTIVE;
4408 element = EL_AMOEBA_WET;
4412 element = EL_AMOEBA_DROP;
4416 element = EL_DC_MAGIC_WALL;
4420 element = EL_SPACESHIP_UP;
4424 element = EL_SPACESHIP_DOWN;
4428 element = EL_SPACESHIP_LEFT;
4432 element = EL_SPACESHIP_RIGHT;
4436 element = EL_BUG_UP;
4440 element = EL_BUG_DOWN;
4444 element = EL_BUG_LEFT;
4448 element = EL_BUG_RIGHT;
4452 element = EL_MOLE_UP;
4456 element = EL_MOLE_DOWN;
4460 element = EL_MOLE_LEFT;
4464 element = EL_MOLE_RIGHT;
4472 element = EL_YAMYAM_UP;
4476 element = EL_SWITCHGATE_OPEN;
4480 element = EL_SWITCHGATE_CLOSED;
4484 element = EL_DC_SWITCHGATE_SWITCH_UP;
4488 element = EL_TIMEGATE_CLOSED;
4491 case 0x144c: // conveyor belt switch (green)
4492 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4495 case 0x144f: // conveyor belt switch (red)
4496 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4499 case 0x1452: // conveyor belt switch (blue)
4500 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4504 element = EL_CONVEYOR_BELT_3_MIDDLE;
4508 element = EL_CONVEYOR_BELT_3_LEFT;
4512 element = EL_CONVEYOR_BELT_3_RIGHT;
4516 element = EL_CONVEYOR_BELT_1_MIDDLE;
4520 element = EL_CONVEYOR_BELT_1_LEFT;
4524 element = EL_CONVEYOR_BELT_1_RIGHT;
4528 element = EL_CONVEYOR_BELT_4_MIDDLE;
4532 element = EL_CONVEYOR_BELT_4_LEFT;
4536 element = EL_CONVEYOR_BELT_4_RIGHT;
4540 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4544 element = EL_EXPANDABLE_WALL_VERTICAL;
4548 element = EL_EXPANDABLE_WALL_ANY;
4551 case 0x14ce: // growing steel wall (left/right)
4552 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4555 case 0x14df: // growing steel wall (up/down)
4556 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4559 case 0x14e8: // growing steel wall (up/down/left/right)
4560 element = EL_EXPANDABLE_STEELWALL_ANY;
4564 element = EL_SHIELD_DEADLY;
4568 element = EL_EXTRA_TIME;
4576 element = EL_EMPTY_SPACE;
4579 case 0x1578: // quicksand (empty)
4580 element = EL_QUICKSAND_FAST_EMPTY;
4583 case 0x1579: // slow quicksand (empty)
4584 element = EL_QUICKSAND_EMPTY;
4594 element = EL_EM_DYNAMITE;
4597 case 0x15a1: // key (red)
4598 element = EL_EM_KEY_1;
4601 case 0x15a2: // key (yellow)
4602 element = EL_EM_KEY_2;
4605 case 0x15a3: // key (blue)
4606 element = EL_EM_KEY_4;
4609 case 0x15a4: // key (green)
4610 element = EL_EM_KEY_3;
4613 case 0x15a5: // key (white)
4614 element = EL_DC_KEY_WHITE;
4618 element = EL_WALL_SLIPPERY;
4625 case 0x15a8: // wall (not round)
4629 case 0x15a9: // (blue)
4630 element = EL_CHAR_A;
4633 case 0x15aa: // (blue)
4634 element = EL_CHAR_B;
4637 case 0x15ab: // (blue)
4638 element = EL_CHAR_C;
4641 case 0x15ac: // (blue)
4642 element = EL_CHAR_D;
4645 case 0x15ad: // (blue)
4646 element = EL_CHAR_E;
4649 case 0x15ae: // (blue)
4650 element = EL_CHAR_F;
4653 case 0x15af: // (blue)
4654 element = EL_CHAR_G;
4657 case 0x15b0: // (blue)
4658 element = EL_CHAR_H;
4661 case 0x15b1: // (blue)
4662 element = EL_CHAR_I;
4665 case 0x15b2: // (blue)
4666 element = EL_CHAR_J;
4669 case 0x15b3: // (blue)
4670 element = EL_CHAR_K;
4673 case 0x15b4: // (blue)
4674 element = EL_CHAR_L;
4677 case 0x15b5: // (blue)
4678 element = EL_CHAR_M;
4681 case 0x15b6: // (blue)
4682 element = EL_CHAR_N;
4685 case 0x15b7: // (blue)
4686 element = EL_CHAR_O;
4689 case 0x15b8: // (blue)
4690 element = EL_CHAR_P;
4693 case 0x15b9: // (blue)
4694 element = EL_CHAR_Q;
4697 case 0x15ba: // (blue)
4698 element = EL_CHAR_R;
4701 case 0x15bb: // (blue)
4702 element = EL_CHAR_S;
4705 case 0x15bc: // (blue)
4706 element = EL_CHAR_T;
4709 case 0x15bd: // (blue)
4710 element = EL_CHAR_U;
4713 case 0x15be: // (blue)
4714 element = EL_CHAR_V;
4717 case 0x15bf: // (blue)
4718 element = EL_CHAR_W;
4721 case 0x15c0: // (blue)
4722 element = EL_CHAR_X;
4725 case 0x15c1: // (blue)
4726 element = EL_CHAR_Y;
4729 case 0x15c2: // (blue)
4730 element = EL_CHAR_Z;
4733 case 0x15c3: // (blue)
4734 element = EL_CHAR_AUMLAUT;
4737 case 0x15c4: // (blue)
4738 element = EL_CHAR_OUMLAUT;
4741 case 0x15c5: // (blue)
4742 element = EL_CHAR_UUMLAUT;
4745 case 0x15c6: // (blue)
4746 element = EL_CHAR_0;
4749 case 0x15c7: // (blue)
4750 element = EL_CHAR_1;
4753 case 0x15c8: // (blue)
4754 element = EL_CHAR_2;
4757 case 0x15c9: // (blue)
4758 element = EL_CHAR_3;
4761 case 0x15ca: // (blue)
4762 element = EL_CHAR_4;
4765 case 0x15cb: // (blue)
4766 element = EL_CHAR_5;
4769 case 0x15cc: // (blue)
4770 element = EL_CHAR_6;
4773 case 0x15cd: // (blue)
4774 element = EL_CHAR_7;
4777 case 0x15ce: // (blue)
4778 element = EL_CHAR_8;
4781 case 0x15cf: // (blue)
4782 element = EL_CHAR_9;
4785 case 0x15d0: // (blue)
4786 element = EL_CHAR_PERIOD;
4789 case 0x15d1: // (blue)
4790 element = EL_CHAR_EXCLAM;
4793 case 0x15d2: // (blue)
4794 element = EL_CHAR_COLON;
4797 case 0x15d3: // (blue)
4798 element = EL_CHAR_LESS;
4801 case 0x15d4: // (blue)
4802 element = EL_CHAR_GREATER;
4805 case 0x15d5: // (blue)
4806 element = EL_CHAR_QUESTION;
4809 case 0x15d6: // (blue)
4810 element = EL_CHAR_COPYRIGHT;
4813 case 0x15d7: // (blue)
4814 element = EL_CHAR_UP;
4817 case 0x15d8: // (blue)
4818 element = EL_CHAR_DOWN;
4821 case 0x15d9: // (blue)
4822 element = EL_CHAR_BUTTON;
4825 case 0x15da: // (blue)
4826 element = EL_CHAR_PLUS;
4829 case 0x15db: // (blue)
4830 element = EL_CHAR_MINUS;
4833 case 0x15dc: // (blue)
4834 element = EL_CHAR_APOSTROPHE;
4837 case 0x15dd: // (blue)
4838 element = EL_CHAR_PARENLEFT;
4841 case 0x15de: // (blue)
4842 element = EL_CHAR_PARENRIGHT;
4845 case 0x15df: // (green)
4846 element = EL_CHAR_A;
4849 case 0x15e0: // (green)
4850 element = EL_CHAR_B;
4853 case 0x15e1: // (green)
4854 element = EL_CHAR_C;
4857 case 0x15e2: // (green)
4858 element = EL_CHAR_D;
4861 case 0x15e3: // (green)
4862 element = EL_CHAR_E;
4865 case 0x15e4: // (green)
4866 element = EL_CHAR_F;
4869 case 0x15e5: // (green)
4870 element = EL_CHAR_G;
4873 case 0x15e6: // (green)
4874 element = EL_CHAR_H;
4877 case 0x15e7: // (green)
4878 element = EL_CHAR_I;
4881 case 0x15e8: // (green)
4882 element = EL_CHAR_J;
4885 case 0x15e9: // (green)
4886 element = EL_CHAR_K;
4889 case 0x15ea: // (green)
4890 element = EL_CHAR_L;
4893 case 0x15eb: // (green)
4894 element = EL_CHAR_M;
4897 case 0x15ec: // (green)
4898 element = EL_CHAR_N;
4901 case 0x15ed: // (green)
4902 element = EL_CHAR_O;
4905 case 0x15ee: // (green)
4906 element = EL_CHAR_P;
4909 case 0x15ef: // (green)
4910 element = EL_CHAR_Q;
4913 case 0x15f0: // (green)
4914 element = EL_CHAR_R;
4917 case 0x15f1: // (green)
4918 element = EL_CHAR_S;
4921 case 0x15f2: // (green)
4922 element = EL_CHAR_T;
4925 case 0x15f3: // (green)
4926 element = EL_CHAR_U;
4929 case 0x15f4: // (green)
4930 element = EL_CHAR_V;
4933 case 0x15f5: // (green)
4934 element = EL_CHAR_W;
4937 case 0x15f6: // (green)
4938 element = EL_CHAR_X;
4941 case 0x15f7: // (green)
4942 element = EL_CHAR_Y;
4945 case 0x15f8: // (green)
4946 element = EL_CHAR_Z;
4949 case 0x15f9: // (green)
4950 element = EL_CHAR_AUMLAUT;
4953 case 0x15fa: // (green)
4954 element = EL_CHAR_OUMLAUT;
4957 case 0x15fb: // (green)
4958 element = EL_CHAR_UUMLAUT;
4961 case 0x15fc: // (green)
4962 element = EL_CHAR_0;
4965 case 0x15fd: // (green)
4966 element = EL_CHAR_1;
4969 case 0x15fe: // (green)
4970 element = EL_CHAR_2;
4973 case 0x15ff: // (green)
4974 element = EL_CHAR_3;
4977 case 0x1600: // (green)
4978 element = EL_CHAR_4;
4981 case 0x1601: // (green)
4982 element = EL_CHAR_5;
4985 case 0x1602: // (green)
4986 element = EL_CHAR_6;
4989 case 0x1603: // (green)
4990 element = EL_CHAR_7;
4993 case 0x1604: // (green)
4994 element = EL_CHAR_8;
4997 case 0x1605: // (green)
4998 element = EL_CHAR_9;
5001 case 0x1606: // (green)
5002 element = EL_CHAR_PERIOD;
5005 case 0x1607: // (green)
5006 element = EL_CHAR_EXCLAM;
5009 case 0x1608: // (green)
5010 element = EL_CHAR_COLON;
5013 case 0x1609: // (green)
5014 element = EL_CHAR_LESS;
5017 case 0x160a: // (green)
5018 element = EL_CHAR_GREATER;
5021 case 0x160b: // (green)
5022 element = EL_CHAR_QUESTION;
5025 case 0x160c: // (green)
5026 element = EL_CHAR_COPYRIGHT;
5029 case 0x160d: // (green)
5030 element = EL_CHAR_UP;
5033 case 0x160e: // (green)
5034 element = EL_CHAR_DOWN;
5037 case 0x160f: // (green)
5038 element = EL_CHAR_BUTTON;
5041 case 0x1610: // (green)
5042 element = EL_CHAR_PLUS;
5045 case 0x1611: // (green)
5046 element = EL_CHAR_MINUS;
5049 case 0x1612: // (green)
5050 element = EL_CHAR_APOSTROPHE;
5053 case 0x1613: // (green)
5054 element = EL_CHAR_PARENLEFT;
5057 case 0x1614: // (green)
5058 element = EL_CHAR_PARENRIGHT;
5061 case 0x1615: // (blue steel)
5062 element = EL_STEEL_CHAR_A;
5065 case 0x1616: // (blue steel)
5066 element = EL_STEEL_CHAR_B;
5069 case 0x1617: // (blue steel)
5070 element = EL_STEEL_CHAR_C;
5073 case 0x1618: // (blue steel)
5074 element = EL_STEEL_CHAR_D;
5077 case 0x1619: // (blue steel)
5078 element = EL_STEEL_CHAR_E;
5081 case 0x161a: // (blue steel)
5082 element = EL_STEEL_CHAR_F;
5085 case 0x161b: // (blue steel)
5086 element = EL_STEEL_CHAR_G;
5089 case 0x161c: // (blue steel)
5090 element = EL_STEEL_CHAR_H;
5093 case 0x161d: // (blue steel)
5094 element = EL_STEEL_CHAR_I;
5097 case 0x161e: // (blue steel)
5098 element = EL_STEEL_CHAR_J;
5101 case 0x161f: // (blue steel)
5102 element = EL_STEEL_CHAR_K;
5105 case 0x1620: // (blue steel)
5106 element = EL_STEEL_CHAR_L;
5109 case 0x1621: // (blue steel)
5110 element = EL_STEEL_CHAR_M;
5113 case 0x1622: // (blue steel)
5114 element = EL_STEEL_CHAR_N;
5117 case 0x1623: // (blue steel)
5118 element = EL_STEEL_CHAR_O;
5121 case 0x1624: // (blue steel)
5122 element = EL_STEEL_CHAR_P;
5125 case 0x1625: // (blue steel)
5126 element = EL_STEEL_CHAR_Q;
5129 case 0x1626: // (blue steel)
5130 element = EL_STEEL_CHAR_R;
5133 case 0x1627: // (blue steel)
5134 element = EL_STEEL_CHAR_S;
5137 case 0x1628: // (blue steel)
5138 element = EL_STEEL_CHAR_T;
5141 case 0x1629: // (blue steel)
5142 element = EL_STEEL_CHAR_U;
5145 case 0x162a: // (blue steel)
5146 element = EL_STEEL_CHAR_V;
5149 case 0x162b: // (blue steel)
5150 element = EL_STEEL_CHAR_W;
5153 case 0x162c: // (blue steel)
5154 element = EL_STEEL_CHAR_X;
5157 case 0x162d: // (blue steel)
5158 element = EL_STEEL_CHAR_Y;
5161 case 0x162e: // (blue steel)
5162 element = EL_STEEL_CHAR_Z;
5165 case 0x162f: // (blue steel)
5166 element = EL_STEEL_CHAR_AUMLAUT;
5169 case 0x1630: // (blue steel)
5170 element = EL_STEEL_CHAR_OUMLAUT;
5173 case 0x1631: // (blue steel)
5174 element = EL_STEEL_CHAR_UUMLAUT;
5177 case 0x1632: // (blue steel)
5178 element = EL_STEEL_CHAR_0;
5181 case 0x1633: // (blue steel)
5182 element = EL_STEEL_CHAR_1;
5185 case 0x1634: // (blue steel)
5186 element = EL_STEEL_CHAR_2;
5189 case 0x1635: // (blue steel)
5190 element = EL_STEEL_CHAR_3;
5193 case 0x1636: // (blue steel)
5194 element = EL_STEEL_CHAR_4;
5197 case 0x1637: // (blue steel)
5198 element = EL_STEEL_CHAR_5;
5201 case 0x1638: // (blue steel)
5202 element = EL_STEEL_CHAR_6;
5205 case 0x1639: // (blue steel)
5206 element = EL_STEEL_CHAR_7;
5209 case 0x163a: // (blue steel)
5210 element = EL_STEEL_CHAR_8;
5213 case 0x163b: // (blue steel)
5214 element = EL_STEEL_CHAR_9;
5217 case 0x163c: // (blue steel)
5218 element = EL_STEEL_CHAR_PERIOD;
5221 case 0x163d: // (blue steel)
5222 element = EL_STEEL_CHAR_EXCLAM;
5225 case 0x163e: // (blue steel)
5226 element = EL_STEEL_CHAR_COLON;
5229 case 0x163f: // (blue steel)
5230 element = EL_STEEL_CHAR_LESS;
5233 case 0x1640: // (blue steel)
5234 element = EL_STEEL_CHAR_GREATER;
5237 case 0x1641: // (blue steel)
5238 element = EL_STEEL_CHAR_QUESTION;
5241 case 0x1642: // (blue steel)
5242 element = EL_STEEL_CHAR_COPYRIGHT;
5245 case 0x1643: // (blue steel)
5246 element = EL_STEEL_CHAR_UP;
5249 case 0x1644: // (blue steel)
5250 element = EL_STEEL_CHAR_DOWN;
5253 case 0x1645: // (blue steel)
5254 element = EL_STEEL_CHAR_BUTTON;
5257 case 0x1646: // (blue steel)
5258 element = EL_STEEL_CHAR_PLUS;
5261 case 0x1647: // (blue steel)
5262 element = EL_STEEL_CHAR_MINUS;
5265 case 0x1648: // (blue steel)
5266 element = EL_STEEL_CHAR_APOSTROPHE;
5269 case 0x1649: // (blue steel)
5270 element = EL_STEEL_CHAR_PARENLEFT;
5273 case 0x164a: // (blue steel)
5274 element = EL_STEEL_CHAR_PARENRIGHT;
5277 case 0x164b: // (green steel)
5278 element = EL_STEEL_CHAR_A;
5281 case 0x164c: // (green steel)
5282 element = EL_STEEL_CHAR_B;
5285 case 0x164d: // (green steel)
5286 element = EL_STEEL_CHAR_C;
5289 case 0x164e: // (green steel)
5290 element = EL_STEEL_CHAR_D;
5293 case 0x164f: // (green steel)
5294 element = EL_STEEL_CHAR_E;
5297 case 0x1650: // (green steel)
5298 element = EL_STEEL_CHAR_F;
5301 case 0x1651: // (green steel)
5302 element = EL_STEEL_CHAR_G;
5305 case 0x1652: // (green steel)
5306 element = EL_STEEL_CHAR_H;
5309 case 0x1653: // (green steel)
5310 element = EL_STEEL_CHAR_I;
5313 case 0x1654: // (green steel)
5314 element = EL_STEEL_CHAR_J;
5317 case 0x1655: // (green steel)
5318 element = EL_STEEL_CHAR_K;
5321 case 0x1656: // (green steel)
5322 element = EL_STEEL_CHAR_L;
5325 case 0x1657: // (green steel)
5326 element = EL_STEEL_CHAR_M;
5329 case 0x1658: // (green steel)
5330 element = EL_STEEL_CHAR_N;
5333 case 0x1659: // (green steel)
5334 element = EL_STEEL_CHAR_O;
5337 case 0x165a: // (green steel)
5338 element = EL_STEEL_CHAR_P;
5341 case 0x165b: // (green steel)
5342 element = EL_STEEL_CHAR_Q;
5345 case 0x165c: // (green steel)
5346 element = EL_STEEL_CHAR_R;
5349 case 0x165d: // (green steel)
5350 element = EL_STEEL_CHAR_S;
5353 case 0x165e: // (green steel)
5354 element = EL_STEEL_CHAR_T;
5357 case 0x165f: // (green steel)
5358 element = EL_STEEL_CHAR_U;
5361 case 0x1660: // (green steel)
5362 element = EL_STEEL_CHAR_V;
5365 case 0x1661: // (green steel)
5366 element = EL_STEEL_CHAR_W;
5369 case 0x1662: // (green steel)
5370 element = EL_STEEL_CHAR_X;
5373 case 0x1663: // (green steel)
5374 element = EL_STEEL_CHAR_Y;
5377 case 0x1664: // (green steel)
5378 element = EL_STEEL_CHAR_Z;
5381 case 0x1665: // (green steel)
5382 element = EL_STEEL_CHAR_AUMLAUT;
5385 case 0x1666: // (green steel)
5386 element = EL_STEEL_CHAR_OUMLAUT;
5389 case 0x1667: // (green steel)
5390 element = EL_STEEL_CHAR_UUMLAUT;
5393 case 0x1668: // (green steel)
5394 element = EL_STEEL_CHAR_0;
5397 case 0x1669: // (green steel)
5398 element = EL_STEEL_CHAR_1;
5401 case 0x166a: // (green steel)
5402 element = EL_STEEL_CHAR_2;
5405 case 0x166b: // (green steel)
5406 element = EL_STEEL_CHAR_3;
5409 case 0x166c: // (green steel)
5410 element = EL_STEEL_CHAR_4;
5413 case 0x166d: // (green steel)
5414 element = EL_STEEL_CHAR_5;
5417 case 0x166e: // (green steel)
5418 element = EL_STEEL_CHAR_6;
5421 case 0x166f: // (green steel)
5422 element = EL_STEEL_CHAR_7;
5425 case 0x1670: // (green steel)
5426 element = EL_STEEL_CHAR_8;
5429 case 0x1671: // (green steel)
5430 element = EL_STEEL_CHAR_9;
5433 case 0x1672: // (green steel)
5434 element = EL_STEEL_CHAR_PERIOD;
5437 case 0x1673: // (green steel)
5438 element = EL_STEEL_CHAR_EXCLAM;
5441 case 0x1674: // (green steel)
5442 element = EL_STEEL_CHAR_COLON;
5445 case 0x1675: // (green steel)
5446 element = EL_STEEL_CHAR_LESS;
5449 case 0x1676: // (green steel)
5450 element = EL_STEEL_CHAR_GREATER;
5453 case 0x1677: // (green steel)
5454 element = EL_STEEL_CHAR_QUESTION;
5457 case 0x1678: // (green steel)
5458 element = EL_STEEL_CHAR_COPYRIGHT;
5461 case 0x1679: // (green steel)
5462 element = EL_STEEL_CHAR_UP;
5465 case 0x167a: // (green steel)
5466 element = EL_STEEL_CHAR_DOWN;
5469 case 0x167b: // (green steel)
5470 element = EL_STEEL_CHAR_BUTTON;
5473 case 0x167c: // (green steel)
5474 element = EL_STEEL_CHAR_PLUS;
5477 case 0x167d: // (green steel)
5478 element = EL_STEEL_CHAR_MINUS;
5481 case 0x167e: // (green steel)
5482 element = EL_STEEL_CHAR_APOSTROPHE;
5485 case 0x167f: // (green steel)
5486 element = EL_STEEL_CHAR_PARENLEFT;
5489 case 0x1680: // (green steel)
5490 element = EL_STEEL_CHAR_PARENRIGHT;
5493 case 0x1681: // gate (red)
5494 element = EL_EM_GATE_1;
5497 case 0x1682: // secret gate (red)
5498 element = EL_EM_GATE_1_GRAY;
5501 case 0x1683: // gate (yellow)
5502 element = EL_EM_GATE_2;
5505 case 0x1684: // secret gate (yellow)
5506 element = EL_EM_GATE_2_GRAY;
5509 case 0x1685: // gate (blue)
5510 element = EL_EM_GATE_4;
5513 case 0x1686: // secret gate (blue)
5514 element = EL_EM_GATE_4_GRAY;
5517 case 0x1687: // gate (green)
5518 element = EL_EM_GATE_3;
5521 case 0x1688: // secret gate (green)
5522 element = EL_EM_GATE_3_GRAY;
5525 case 0x1689: // gate (white)
5526 element = EL_DC_GATE_WHITE;
5529 case 0x168a: // secret gate (white)
5530 element = EL_DC_GATE_WHITE_GRAY;
5533 case 0x168b: // secret gate (no key)
5534 element = EL_DC_GATE_FAKE_GRAY;
5538 element = EL_ROBOT_WHEEL;
5542 element = EL_DC_TIMEGATE_SWITCH;
5546 element = EL_ACID_POOL_BOTTOM;
5550 element = EL_ACID_POOL_TOPLEFT;
5554 element = EL_ACID_POOL_TOPRIGHT;
5558 element = EL_ACID_POOL_BOTTOMLEFT;
5562 element = EL_ACID_POOL_BOTTOMRIGHT;
5566 element = EL_STEELWALL;
5570 element = EL_STEELWALL_SLIPPERY;
5573 case 0x1695: // steel wall (not round)
5574 element = EL_STEELWALL;
5577 case 0x1696: // steel wall (left)
5578 element = EL_DC_STEELWALL_1_LEFT;
5581 case 0x1697: // steel wall (bottom)
5582 element = EL_DC_STEELWALL_1_BOTTOM;
5585 case 0x1698: // steel wall (right)
5586 element = EL_DC_STEELWALL_1_RIGHT;
5589 case 0x1699: // steel wall (top)
5590 element = EL_DC_STEELWALL_1_TOP;
5593 case 0x169a: // steel wall (left/bottom)
5594 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5597 case 0x169b: // steel wall (right/bottom)
5598 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5601 case 0x169c: // steel wall (right/top)
5602 element = EL_DC_STEELWALL_1_TOPRIGHT;
5605 case 0x169d: // steel wall (left/top)
5606 element = EL_DC_STEELWALL_1_TOPLEFT;
5609 case 0x169e: // steel wall (right/bottom small)
5610 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5613 case 0x169f: // steel wall (left/bottom small)
5614 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5617 case 0x16a0: // steel wall (right/top small)
5618 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5621 case 0x16a1: // steel wall (left/top small)
5622 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5625 case 0x16a2: // steel wall (left/right)
5626 element = EL_DC_STEELWALL_1_VERTICAL;
5629 case 0x16a3: // steel wall (top/bottom)
5630 element = EL_DC_STEELWALL_1_HORIZONTAL;
5633 case 0x16a4: // steel wall 2 (left end)
5634 element = EL_DC_STEELWALL_2_LEFT;
5637 case 0x16a5: // steel wall 2 (right end)
5638 element = EL_DC_STEELWALL_2_RIGHT;
5641 case 0x16a6: // steel wall 2 (top end)
5642 element = EL_DC_STEELWALL_2_TOP;
5645 case 0x16a7: // steel wall 2 (bottom end)
5646 element = EL_DC_STEELWALL_2_BOTTOM;
5649 case 0x16a8: // steel wall 2 (left/right)
5650 element = EL_DC_STEELWALL_2_HORIZONTAL;
5653 case 0x16a9: // steel wall 2 (up/down)
5654 element = EL_DC_STEELWALL_2_VERTICAL;
5657 case 0x16aa: // steel wall 2 (mid)
5658 element = EL_DC_STEELWALL_2_MIDDLE;
5662 element = EL_SIGN_EXCLAMATION;
5666 element = EL_SIGN_RADIOACTIVITY;
5670 element = EL_SIGN_STOP;
5674 element = EL_SIGN_WHEELCHAIR;
5678 element = EL_SIGN_PARKING;
5682 element = EL_SIGN_NO_ENTRY;
5686 element = EL_SIGN_HEART;
5690 element = EL_SIGN_GIVE_WAY;
5694 element = EL_SIGN_ENTRY_FORBIDDEN;
5698 element = EL_SIGN_EMERGENCY_EXIT;
5702 element = EL_SIGN_YIN_YANG;
5706 element = EL_WALL_EMERALD;
5710 element = EL_WALL_DIAMOND;
5714 element = EL_WALL_PEARL;
5718 element = EL_WALL_CRYSTAL;
5722 element = EL_INVISIBLE_WALL;
5726 element = EL_INVISIBLE_STEELWALL;
5730 // EL_INVISIBLE_SAND
5733 element = EL_LIGHT_SWITCH;
5737 element = EL_ENVELOPE_1;
5741 if (element >= 0x0117 && element <= 0x036e) // (?)
5742 element = EL_DIAMOND;
5743 else if (element >= 0x042d && element <= 0x0684) // (?)
5744 element = EL_EMERALD;
5745 else if (element >= 0x157c && element <= 0x158b)
5747 else if (element >= 0x1590 && element <= 0x159f)
5748 element = EL_DC_LANDMINE;
5749 else if (element >= 0x16bc && element <= 0x16cb)
5750 element = EL_INVISIBLE_SAND;
5753 Warn("unknown Diamond Caves element 0x%04x", element);
5755 element = EL_UNKNOWN;
5760 return getMappedElement(element);
5763 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5765 byte header[DC_LEVEL_HEADER_SIZE];
5767 int envelope_header_pos = 62;
5768 int envelope_content_pos = 94;
5769 int level_name_pos = 251;
5770 int level_author_pos = 292;
5771 int envelope_header_len;
5772 int envelope_content_len;
5774 int level_author_len;
5776 int num_yamyam_contents;
5779 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5781 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5783 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5785 header[i * 2 + 0] = header_word >> 8;
5786 header[i * 2 + 1] = header_word & 0xff;
5789 // read some values from level header to check level decoding integrity
5790 fieldx = header[6] | (header[7] << 8);
5791 fieldy = header[8] | (header[9] << 8);
5792 num_yamyam_contents = header[60] | (header[61] << 8);
5794 // do some simple sanity checks to ensure that level was correctly decoded
5795 if (fieldx < 1 || fieldx > 256 ||
5796 fieldy < 1 || fieldy > 256 ||
5797 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5799 level->no_valid_file = TRUE;
5801 Warn("cannot decode level from stream -- using empty level");
5806 // maximum envelope header size is 31 bytes
5807 envelope_header_len = header[envelope_header_pos];
5808 // maximum envelope content size is 110 (156?) bytes
5809 envelope_content_len = header[envelope_content_pos];
5811 // maximum level title size is 40 bytes
5812 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5813 // maximum level author size is 30 (51?) bytes
5814 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5818 for (i = 0; i < envelope_header_len; i++)
5819 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5820 level->envelope[0].text[envelope_size++] =
5821 header[envelope_header_pos + 1 + i];
5823 if (envelope_header_len > 0 && envelope_content_len > 0)
5825 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5826 level->envelope[0].text[envelope_size++] = '\n';
5827 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5828 level->envelope[0].text[envelope_size++] = '\n';
5831 for (i = 0; i < envelope_content_len; i++)
5832 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5833 level->envelope[0].text[envelope_size++] =
5834 header[envelope_content_pos + 1 + i];
5836 level->envelope[0].text[envelope_size] = '\0';
5838 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5839 level->envelope[0].ysize = 10;
5840 level->envelope[0].autowrap = TRUE;
5841 level->envelope[0].centered = TRUE;
5843 for (i = 0; i < level_name_len; i++)
5844 level->name[i] = header[level_name_pos + 1 + i];
5845 level->name[level_name_len] = '\0';
5847 for (i = 0; i < level_author_len; i++)
5848 level->author[i] = header[level_author_pos + 1 + i];
5849 level->author[level_author_len] = '\0';
5851 num_yamyam_contents = header[60] | (header[61] << 8);
5852 level->num_yamyam_contents =
5853 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5855 for (i = 0; i < num_yamyam_contents; i++)
5857 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5859 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5860 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5862 if (i < MAX_ELEMENT_CONTENTS)
5863 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5867 fieldx = header[6] | (header[7] << 8);
5868 fieldy = header[8] | (header[9] << 8);
5869 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5870 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5872 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5874 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5875 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5877 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5878 level->field[x][y] = getMappedElement_DC(element_dc);
5881 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5882 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5883 level->field[x][y] = EL_PLAYER_1;
5885 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5886 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5887 level->field[x][y] = EL_PLAYER_2;
5889 level->gems_needed = header[18] | (header[19] << 8);
5891 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5892 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5893 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5894 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5895 level->score[SC_NUT] = header[28] | (header[29] << 8);
5896 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5897 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5898 level->score[SC_BUG] = header[34] | (header[35] << 8);
5899 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5900 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5901 level->score[SC_KEY] = header[40] | (header[41] << 8);
5902 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5904 level->time = header[44] | (header[45] << 8);
5906 level->amoeba_speed = header[46] | (header[47] << 8);
5907 level->time_light = header[48] | (header[49] << 8);
5908 level->time_timegate = header[50] | (header[51] << 8);
5909 level->time_wheel = header[52] | (header[53] << 8);
5910 level->time_magic_wall = header[54] | (header[55] << 8);
5911 level->extra_time = header[56] | (header[57] << 8);
5912 level->shield_normal_time = header[58] | (header[59] << 8);
5914 // shield and extra time elements do not have a score
5915 level->score[SC_SHIELD] = 0;
5916 level->extra_time_score = 0;
5918 // set time for normal and deadly shields to the same value
5919 level->shield_deadly_time = level->shield_normal_time;
5921 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5922 // can slip down from flat walls, like normal walls and steel walls
5923 level->em_slippery_gems = TRUE;
5925 // time score is counted for each 10 seconds left in Diamond Caves levels
5926 level->time_score_base = 10;
5929 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5930 struct LevelFileInfo *level_file_info,
5931 boolean level_info_only)
5933 char *filename = level_file_info->filename;
5935 int num_magic_bytes = 8;
5936 char magic_bytes[num_magic_bytes + 1];
5937 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5939 if (!(file = openFile(filename, MODE_READ)))
5941 level->no_valid_file = TRUE;
5943 if (!level_info_only)
5944 Warn("cannot read level '%s' -- using empty level", filename);
5949 // fseek(file, 0x0000, SEEK_SET);
5951 if (level_file_info->packed)
5953 // read "magic bytes" from start of file
5954 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5955 magic_bytes[0] = '\0';
5957 // check "magic bytes" for correct file format
5958 if (!strPrefix(magic_bytes, "DC2"))
5960 level->no_valid_file = TRUE;
5962 Warn("unknown DC level file '%s' -- using empty level", filename);
5967 if (strPrefix(magic_bytes, "DC2Win95") ||
5968 strPrefix(magic_bytes, "DC2Win98"))
5970 int position_first_level = 0x00fa;
5971 int extra_bytes = 4;
5974 // advance file stream to first level inside the level package
5975 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5977 // each block of level data is followed by block of non-level data
5978 num_levels_to_skip *= 2;
5980 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5981 while (num_levels_to_skip >= 0)
5983 // advance file stream to next level inside the level package
5984 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5986 level->no_valid_file = TRUE;
5988 Warn("cannot fseek in file '%s' -- using empty level", filename);
5993 // skip apparently unused extra bytes following each level
5994 ReadUnusedBytesFromFile(file, extra_bytes);
5996 // read size of next level in level package
5997 skip_bytes = getFile32BitLE(file);
5999 num_levels_to_skip--;
6004 level->no_valid_file = TRUE;
6006 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6012 LoadLevelFromFileStream_DC(file, level);
6018 // ----------------------------------------------------------------------------
6019 // functions for loading SB level
6020 // ----------------------------------------------------------------------------
6022 int getMappedElement_SB(int element_ascii, boolean use_ces)
6030 sb_element_mapping[] =
6032 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6033 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6034 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6035 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6036 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6037 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6038 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6039 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6046 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6047 if (element_ascii == sb_element_mapping[i].ascii)
6048 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6050 return EL_UNDEFINED;
6053 static void SetLevelSettings_SB(struct LevelInfo *level)
6057 level->use_step_counter = TRUE;
6060 level->score[SC_TIME_BONUS] = 0;
6061 level->time_score_base = 1;
6062 level->rate_time_over_score = TRUE;
6065 level->auto_exit_sokoban = TRUE;
6068 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6069 struct LevelFileInfo *level_file_info,
6070 boolean level_info_only)
6072 char *filename = level_file_info->filename;
6073 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6074 char last_comment[MAX_LINE_LEN];
6075 char level_name[MAX_LINE_LEN];
6078 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6079 boolean read_continued_line = FALSE;
6080 boolean reading_playfield = FALSE;
6081 boolean got_valid_playfield_line = FALSE;
6082 boolean invalid_playfield_char = FALSE;
6083 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6084 int file_level_nr = 0;
6086 int x = 0, y = 0; // initialized to make compilers happy
6088 last_comment[0] = '\0';
6089 level_name[0] = '\0';
6091 if (!(file = openFile(filename, MODE_READ)))
6093 level->no_valid_file = TRUE;
6095 if (!level_info_only)
6096 Warn("cannot read level '%s' -- using empty level", filename);
6101 while (!checkEndOfFile(file))
6103 // level successfully read, but next level may follow here
6104 if (!got_valid_playfield_line && reading_playfield)
6106 // read playfield from single level file -- skip remaining file
6107 if (!level_file_info->packed)
6110 if (file_level_nr >= num_levels_to_skip)
6115 last_comment[0] = '\0';
6116 level_name[0] = '\0';
6118 reading_playfield = FALSE;
6121 got_valid_playfield_line = FALSE;
6123 // read next line of input file
6124 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6127 // check if line was completely read and is terminated by line break
6128 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6131 // cut trailing line break (this can be newline and/or carriage return)
6132 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6133 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6136 // copy raw input line for later use (mainly debugging output)
6137 strcpy(line_raw, line);
6139 if (read_continued_line)
6141 // append new line to existing line, if there is enough space
6142 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6143 strcat(previous_line, line_ptr);
6145 strcpy(line, previous_line); // copy storage buffer to line
6147 read_continued_line = FALSE;
6150 // if the last character is '\', continue at next line
6151 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6153 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6154 strcpy(previous_line, line); // copy line to storage buffer
6156 read_continued_line = TRUE;
6162 if (line[0] == '\0')
6165 // extract comment text from comment line
6168 for (line_ptr = line; *line_ptr; line_ptr++)
6169 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6172 strcpy(last_comment, line_ptr);
6177 // extract level title text from line containing level title
6178 if (line[0] == '\'')
6180 strcpy(level_name, &line[1]);
6182 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6183 level_name[strlen(level_name) - 1] = '\0';
6188 // skip lines containing only spaces (or empty lines)
6189 for (line_ptr = line; *line_ptr; line_ptr++)
6190 if (*line_ptr != ' ')
6192 if (*line_ptr == '\0')
6195 // at this point, we have found a line containing part of a playfield
6197 got_valid_playfield_line = TRUE;
6199 if (!reading_playfield)
6201 reading_playfield = TRUE;
6202 invalid_playfield_char = FALSE;
6204 for (x = 0; x < MAX_LEV_FIELDX; x++)
6205 for (y = 0; y < MAX_LEV_FIELDY; y++)
6206 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6211 // start with topmost tile row
6215 // skip playfield line if larger row than allowed
6216 if (y >= MAX_LEV_FIELDY)
6219 // start with leftmost tile column
6222 // read playfield elements from line
6223 for (line_ptr = line; *line_ptr; line_ptr++)
6225 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6227 // stop parsing playfield line if larger column than allowed
6228 if (x >= MAX_LEV_FIELDX)
6231 if (mapped_sb_element == EL_UNDEFINED)
6233 invalid_playfield_char = TRUE;
6238 level->field[x][y] = mapped_sb_element;
6240 // continue with next tile column
6243 level->fieldx = MAX(x, level->fieldx);
6246 if (invalid_playfield_char)
6248 // if first playfield line, treat invalid lines as comment lines
6250 reading_playfield = FALSE;
6255 // continue with next tile row
6263 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6264 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6266 if (!reading_playfield)
6268 level->no_valid_file = TRUE;
6270 Warn("cannot read level '%s' -- using empty level", filename);
6275 if (*level_name != '\0')
6277 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6278 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6280 else if (*last_comment != '\0')
6282 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6283 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6287 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6290 // set all empty fields beyond the border walls to invisible steel wall
6291 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6293 if ((x == 0 || x == level->fieldx - 1 ||
6294 y == 0 || y == level->fieldy - 1) &&
6295 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6296 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6297 level->field, level->fieldx, level->fieldy);
6300 // set special level settings for Sokoban levels
6301 SetLevelSettings_SB(level);
6303 if (load_xsb_to_ces)
6305 // special global settings can now be set in level template
6306 level->use_custom_template = TRUE;
6311 // -------------------------------------------------------------------------
6312 // functions for handling native levels
6313 // -------------------------------------------------------------------------
6315 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6316 struct LevelFileInfo *level_file_info,
6317 boolean level_info_only)
6319 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6320 level->no_valid_file = TRUE;
6323 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6324 struct LevelFileInfo *level_file_info,
6325 boolean level_info_only)
6329 // determine position of requested level inside level package
6330 if (level_file_info->packed)
6331 pos = level_file_info->nr - leveldir_current->first_level;
6333 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6334 level->no_valid_file = TRUE;
6337 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6338 struct LevelFileInfo *level_file_info,
6339 boolean level_info_only)
6341 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6342 level->no_valid_file = TRUE;
6345 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6347 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6348 CopyNativeLevel_RND_to_BD(level);
6349 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6350 CopyNativeLevel_RND_to_EM(level);
6351 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6352 CopyNativeLevel_RND_to_SP(level);
6353 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6354 CopyNativeLevel_RND_to_MM(level);
6357 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6359 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6360 CopyNativeLevel_BD_to_RND(level);
6361 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6362 CopyNativeLevel_EM_to_RND(level);
6363 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6364 CopyNativeLevel_SP_to_RND(level);
6365 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6366 CopyNativeLevel_MM_to_RND(level);
6369 void SaveNativeLevel(struct LevelInfo *level)
6371 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6373 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6374 char *filename = getLevelFilenameFromBasename(basename);
6376 CopyNativeLevel_RND_to_SP(level);
6377 CopyNativeTape_RND_to_SP(level);
6379 SaveNativeLevel_SP(filename);
6384 // ----------------------------------------------------------------------------
6385 // functions for loading generic level
6386 // ----------------------------------------------------------------------------
6388 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6389 struct LevelFileInfo *level_file_info,
6390 boolean level_info_only)
6392 // always start with reliable default values
6393 setLevelInfoToDefaults(level, level_info_only, TRUE);
6395 switch (level_file_info->type)
6397 case LEVEL_FILE_TYPE_RND:
6398 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6401 case LEVEL_FILE_TYPE_EM:
6402 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6403 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6406 case LEVEL_FILE_TYPE_SP:
6407 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6408 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6411 case LEVEL_FILE_TYPE_MM:
6412 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6413 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6416 case LEVEL_FILE_TYPE_DC:
6417 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6420 case LEVEL_FILE_TYPE_SB:
6421 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6425 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6429 // if level file is invalid, restore level structure to default values
6430 if (level->no_valid_file)
6431 setLevelInfoToDefaults(level, level_info_only, FALSE);
6433 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6434 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6436 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6437 CopyNativeLevel_Native_to_RND(level);
6440 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6442 static struct LevelFileInfo level_file_info;
6444 // always start with reliable default values
6445 setFileInfoToDefaults(&level_file_info);
6447 level_file_info.nr = 0; // unknown level number
6448 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6450 setString(&level_file_info.filename, filename);
6452 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6455 static void LoadLevel_InitVersion(struct LevelInfo *level)
6459 if (leveldir_current == NULL) // only when dumping level
6462 // all engine modifications also valid for levels which use latest engine
6463 if (level->game_version < VERSION_IDENT(3,2,0,5))
6465 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6466 level->time_score_base = 10;
6469 if (leveldir_current->latest_engine)
6471 // ---------- use latest game engine --------------------------------------
6473 /* For all levels which are forced to use the latest game engine version
6474 (normally all but user contributed, private and undefined levels), set
6475 the game engine version to the actual version; this allows for actual
6476 corrections in the game engine to take effect for existing, converted
6477 levels (from "classic" or other existing games) to make the emulation
6478 of the corresponding game more accurate, while (hopefully) not breaking
6479 existing levels created from other players. */
6481 level->game_version = GAME_VERSION_ACTUAL;
6483 /* Set special EM style gems behaviour: EM style gems slip down from
6484 normal, steel and growing wall. As this is a more fundamental change,
6485 it seems better to set the default behaviour to "off" (as it is more
6486 natural) and make it configurable in the level editor (as a property
6487 of gem style elements). Already existing converted levels (neither
6488 private nor contributed levels) are changed to the new behaviour. */
6490 if (level->file_version < FILE_VERSION_2_0)
6491 level->em_slippery_gems = TRUE;
6496 // ---------- use game engine the level was created with --------------------
6498 /* For all levels which are not forced to use the latest game engine
6499 version (normally user contributed, private and undefined levels),
6500 use the version of the game engine the levels were created for.
6502 Since 2.0.1, the game engine version is now directly stored
6503 in the level file (chunk "VERS"), so there is no need anymore
6504 to set the game version from the file version (except for old,
6505 pre-2.0 levels, where the game version is still taken from the
6506 file format version used to store the level -- see above). */
6508 // player was faster than enemies in 1.0.0 and before
6509 if (level->file_version == FILE_VERSION_1_0)
6510 for (i = 0; i < MAX_PLAYERS; i++)
6511 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6513 // default behaviour for EM style gems was "slippery" only in 2.0.1
6514 if (level->game_version == VERSION_IDENT(2,0,1,0))
6515 level->em_slippery_gems = TRUE;
6517 // springs could be pushed over pits before (pre-release version) 2.2.0
6518 if (level->game_version < VERSION_IDENT(2,2,0,0))
6519 level->use_spring_bug = TRUE;
6521 if (level->game_version < VERSION_IDENT(3,2,0,5))
6523 // time orb caused limited time in endless time levels before 3.2.0-5
6524 level->use_time_orb_bug = TRUE;
6526 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6527 level->block_snap_field = FALSE;
6529 // extra time score was same value as time left score before 3.2.0-5
6530 level->extra_time_score = level->score[SC_TIME_BONUS];
6533 if (level->game_version < VERSION_IDENT(3,2,0,7))
6535 // default behaviour for snapping was "not continuous" before 3.2.0-7
6536 level->continuous_snapping = FALSE;
6539 // only few elements were able to actively move into acid before 3.1.0
6540 // trigger settings did not exist before 3.1.0; set to default "any"
6541 if (level->game_version < VERSION_IDENT(3,1,0,0))
6543 // correct "can move into acid" settings (all zero in old levels)
6545 level->can_move_into_acid_bits = 0; // nothing can move into acid
6546 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6548 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6549 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6550 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6551 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6553 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6554 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6556 // correct trigger settings (stored as zero == "none" in old levels)
6558 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6560 int element = EL_CUSTOM_START + i;
6561 struct ElementInfo *ei = &element_info[element];
6563 for (j = 0; j < ei->num_change_pages; j++)
6565 struct ElementChangeInfo *change = &ei->change_page[j];
6567 change->trigger_player = CH_PLAYER_ANY;
6568 change->trigger_page = CH_PAGE_ANY;
6573 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6575 int element = EL_CUSTOM_256;
6576 struct ElementInfo *ei = &element_info[element];
6577 struct ElementChangeInfo *change = &ei->change_page[0];
6579 /* This is needed to fix a problem that was caused by a bugfix in function
6580 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6581 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6582 not replace walkable elements, but instead just placed the player on it,
6583 without placing the Sokoban field under the player). Unfortunately, this
6584 breaks "Snake Bite" style levels when the snake is halfway through a door
6585 that just closes (the snake head is still alive and can be moved in this
6586 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6587 player (without Sokoban element) which then gets killed as designed). */
6589 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6590 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6591 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6592 change->target_element = EL_PLAYER_1;
6595 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6596 if (level->game_version < VERSION_IDENT(3,2,5,0))
6598 /* This is needed to fix a problem that was caused by a bugfix in function
6599 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6600 corrects the behaviour when a custom element changes to another custom
6601 element with a higher element number that has change actions defined.
6602 Normally, only one change per frame is allowed for custom elements.
6603 Therefore, it is checked if a custom element already changed in the
6604 current frame; if it did, subsequent changes are suppressed.
6605 Unfortunately, this is only checked for element changes, but not for
6606 change actions, which are still executed. As the function above loops
6607 through all custom elements from lower to higher, an element change
6608 resulting in a lower CE number won't be checked again, while a target
6609 element with a higher number will also be checked, and potential change
6610 actions will get executed for this CE, too (which is wrong), while
6611 further changes are ignored (which is correct). As this bugfix breaks
6612 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6613 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6614 behaviour for existing levels and tapes that make use of this bug */
6616 level->use_action_after_change_bug = TRUE;
6619 // not centering level after relocating player was default only in 3.2.3
6620 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6621 level->shifted_relocation = TRUE;
6623 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6624 if (level->game_version < VERSION_IDENT(3,2,6,0))
6625 level->em_explodes_by_fire = TRUE;
6627 // levels were solved by the first player entering an exit up to 4.1.0.0
6628 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6629 level->solved_by_one_player = TRUE;
6631 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6632 if (level->game_version < VERSION_IDENT(4,1,1,1))
6633 level->use_life_bugs = TRUE;
6635 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6636 if (level->game_version < VERSION_IDENT(4,1,1,1))
6637 level->sb_objects_needed = FALSE;
6639 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6640 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6641 level->finish_dig_collect = FALSE;
6643 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6644 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6645 level->keep_walkable_ce = TRUE;
6648 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6650 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6653 // check if this level is (not) a Sokoban level
6654 for (y = 0; y < level->fieldy; y++)
6655 for (x = 0; x < level->fieldx; x++)
6656 if (!IS_SB_ELEMENT(Tile[x][y]))
6657 is_sokoban_level = FALSE;
6659 if (is_sokoban_level)
6661 // set special level settings for Sokoban levels
6662 SetLevelSettings_SB(level);
6666 static void LoadLevel_InitSettings(struct LevelInfo *level)
6668 // adjust level settings for (non-native) Sokoban-style levels
6669 LoadLevel_InitSettings_SB(level);
6671 // rename levels with title "nameless level" or if renaming is forced
6672 if (leveldir_current->empty_level_name != NULL &&
6673 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6674 leveldir_current->force_level_name))
6675 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6676 leveldir_current->empty_level_name, level_nr);
6679 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6683 // map elements that have changed in newer versions
6684 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6685 level->game_version);
6686 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6687 for (x = 0; x < 3; x++)
6688 for (y = 0; y < 3; y++)
6689 level->yamyam_content[i].e[x][y] =
6690 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6691 level->game_version);
6695 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6699 // map custom element change events that have changed in newer versions
6700 // (these following values were accidentally changed in version 3.0.1)
6701 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6702 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6704 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6706 int element = EL_CUSTOM_START + i;
6708 // order of checking and copying events to be mapped is important
6709 // (do not change the start and end value -- they are constant)
6710 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6712 if (HAS_CHANGE_EVENT(element, j - 2))
6714 SET_CHANGE_EVENT(element, j - 2, FALSE);
6715 SET_CHANGE_EVENT(element, j, TRUE);
6719 // order of checking and copying events to be mapped is important
6720 // (do not change the start and end value -- they are constant)
6721 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6723 if (HAS_CHANGE_EVENT(element, j - 1))
6725 SET_CHANGE_EVENT(element, j - 1, FALSE);
6726 SET_CHANGE_EVENT(element, j, TRUE);
6732 // initialize "can_change" field for old levels with only one change page
6733 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6735 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6737 int element = EL_CUSTOM_START + i;
6739 if (CAN_CHANGE(element))
6740 element_info[element].change->can_change = TRUE;
6744 // correct custom element values (for old levels without these options)
6745 if (level->game_version < VERSION_IDENT(3,1,1,0))
6747 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6749 int element = EL_CUSTOM_START + i;
6750 struct ElementInfo *ei = &element_info[element];
6752 if (ei->access_direction == MV_NO_DIRECTION)
6753 ei->access_direction = MV_ALL_DIRECTIONS;
6757 // correct custom element values (fix invalid values for all versions)
6760 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6762 int element = EL_CUSTOM_START + i;
6763 struct ElementInfo *ei = &element_info[element];
6765 for (j = 0; j < ei->num_change_pages; j++)
6767 struct ElementChangeInfo *change = &ei->change_page[j];
6769 if (change->trigger_player == CH_PLAYER_NONE)
6770 change->trigger_player = CH_PLAYER_ANY;
6772 if (change->trigger_side == CH_SIDE_NONE)
6773 change->trigger_side = CH_SIDE_ANY;
6778 // initialize "can_explode" field for old levels which did not store this
6779 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6780 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6782 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6784 int element = EL_CUSTOM_START + i;
6786 if (EXPLODES_1X1_OLD(element))
6787 element_info[element].explosion_type = EXPLODES_1X1;
6789 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6790 EXPLODES_SMASHED(element) ||
6791 EXPLODES_IMPACT(element)));
6795 // correct previously hard-coded move delay values for maze runner style
6796 if (level->game_version < VERSION_IDENT(3,1,1,0))
6798 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6800 int element = EL_CUSTOM_START + i;
6802 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6804 // previously hard-coded and therefore ignored
6805 element_info[element].move_delay_fixed = 9;
6806 element_info[element].move_delay_random = 0;
6811 // set some other uninitialized values of custom elements in older levels
6812 if (level->game_version < VERSION_IDENT(3,1,0,0))
6814 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6816 int element = EL_CUSTOM_START + i;
6818 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6820 element_info[element].explosion_delay = 17;
6821 element_info[element].ignition_delay = 8;
6825 // set mouse click change events to work for left/middle/right mouse button
6826 if (level->game_version < VERSION_IDENT(4,2,3,0))
6828 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6830 int element = EL_CUSTOM_START + i;
6831 struct ElementInfo *ei = &element_info[element];
6833 for (j = 0; j < ei->num_change_pages; j++)
6835 struct ElementChangeInfo *change = &ei->change_page[j];
6837 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6838 change->has_event[CE_PRESSED_BY_MOUSE] ||
6839 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6840 change->has_event[CE_MOUSE_PRESSED_ON_X])
6841 change->trigger_side = CH_SIDE_ANY;
6847 static void LoadLevel_InitElements(struct LevelInfo *level)
6849 LoadLevel_InitStandardElements(level);
6851 if (level->file_has_custom_elements)
6852 LoadLevel_InitCustomElements(level);
6854 // initialize element properties for level editor etc.
6855 InitElementPropertiesEngine(level->game_version);
6856 InitElementPropertiesGfxElement();
6859 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6863 // map elements that have changed in newer versions
6864 for (y = 0; y < level->fieldy; y++)
6865 for (x = 0; x < level->fieldx; x++)
6866 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6867 level->game_version);
6869 // clear unused playfield data (nicer if level gets resized in editor)
6870 for (x = 0; x < MAX_LEV_FIELDX; x++)
6871 for (y = 0; y < MAX_LEV_FIELDY; y++)
6872 if (x >= level->fieldx || y >= level->fieldy)
6873 level->field[x][y] = EL_EMPTY;
6875 // copy elements to runtime playfield array
6876 for (x = 0; x < MAX_LEV_FIELDX; x++)
6877 for (y = 0; y < MAX_LEV_FIELDY; y++)
6878 Tile[x][y] = level->field[x][y];
6880 // initialize level size variables for faster access
6881 lev_fieldx = level->fieldx;
6882 lev_fieldy = level->fieldy;
6884 // determine border element for this level
6885 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6886 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6891 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6893 struct LevelFileInfo *level_file_info = &level->file_info;
6895 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6896 CopyNativeLevel_RND_to_Native(level);
6899 static void LoadLevelTemplate_LoadAndInit(void)
6901 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6903 LoadLevel_InitVersion(&level_template);
6904 LoadLevel_InitElements(&level_template);
6905 LoadLevel_InitSettings(&level_template);
6907 ActivateLevelTemplate();
6910 void LoadLevelTemplate(int nr)
6912 if (!fileExists(getGlobalLevelTemplateFilename()))
6914 Warn("no level template found for this level");
6919 setLevelFileInfo(&level_template.file_info, nr);
6921 LoadLevelTemplate_LoadAndInit();
6924 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6926 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6928 LoadLevelTemplate_LoadAndInit();
6931 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6933 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6935 if (level.use_custom_template)
6937 if (network_level != NULL)
6938 LoadNetworkLevelTemplate(network_level);
6940 LoadLevelTemplate(-1);
6943 LoadLevel_InitVersion(&level);
6944 LoadLevel_InitElements(&level);
6945 LoadLevel_InitPlayfield(&level);
6946 LoadLevel_InitSettings(&level);
6948 LoadLevel_InitNativeEngines(&level);
6951 void LoadLevel(int nr)
6953 SetLevelSetInfo(leveldir_current->identifier, nr);
6955 setLevelFileInfo(&level.file_info, nr);
6957 LoadLevel_LoadAndInit(NULL);
6960 void LoadLevelInfoOnly(int nr)
6962 setLevelFileInfo(&level.file_info, nr);
6964 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6967 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6969 SetLevelSetInfo(network_level->leveldir_identifier,
6970 network_level->file_info.nr);
6972 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6974 LoadLevel_LoadAndInit(network_level);
6977 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6981 chunk_size += putFileVersion(file, level->file_version);
6982 chunk_size += putFileVersion(file, level->game_version);
6987 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6991 chunk_size += putFile16BitBE(file, level->creation_date.year);
6992 chunk_size += putFile8Bit(file, level->creation_date.month);
6993 chunk_size += putFile8Bit(file, level->creation_date.day);
6998 #if ENABLE_HISTORIC_CHUNKS
6999 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7003 putFile8Bit(file, level->fieldx);
7004 putFile8Bit(file, level->fieldy);
7006 putFile16BitBE(file, level->time);
7007 putFile16BitBE(file, level->gems_needed);
7009 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7010 putFile8Bit(file, level->name[i]);
7012 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7013 putFile8Bit(file, level->score[i]);
7015 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7016 for (y = 0; y < 3; y++)
7017 for (x = 0; x < 3; x++)
7018 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7019 level->yamyam_content[i].e[x][y]));
7020 putFile8Bit(file, level->amoeba_speed);
7021 putFile8Bit(file, level->time_magic_wall);
7022 putFile8Bit(file, level->time_wheel);
7023 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7024 level->amoeba_content));
7025 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7026 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7027 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7028 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7030 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7032 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7033 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7034 putFile32BitBE(file, level->can_move_into_acid_bits);
7035 putFile8Bit(file, level->dont_collide_with_bits);
7037 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7038 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7040 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7041 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7042 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7044 putFile8Bit(file, level->game_engine_type);
7046 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7050 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7055 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7056 chunk_size += putFile8Bit(file, level->name[i]);
7061 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7066 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7067 chunk_size += putFile8Bit(file, level->author[i]);
7072 #if ENABLE_HISTORIC_CHUNKS
7073 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7078 for (y = 0; y < level->fieldy; y++)
7079 for (x = 0; x < level->fieldx; x++)
7080 if (level->encoding_16bit_field)
7081 chunk_size += putFile16BitBE(file, level->field[x][y]);
7083 chunk_size += putFile8Bit(file, level->field[x][y]);
7089 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7094 for (y = 0; y < level->fieldy; y++)
7095 for (x = 0; x < level->fieldx; x++)
7096 chunk_size += putFile16BitBE(file, level->field[x][y]);
7101 #if ENABLE_HISTORIC_CHUNKS
7102 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7106 putFile8Bit(file, EL_YAMYAM);
7107 putFile8Bit(file, level->num_yamyam_contents);
7108 putFile8Bit(file, 0);
7109 putFile8Bit(file, 0);
7111 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7112 for (y = 0; y < 3; y++)
7113 for (x = 0; x < 3; x++)
7114 if (level->encoding_16bit_field)
7115 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7117 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7121 #if ENABLE_HISTORIC_CHUNKS
7122 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7125 int num_contents, content_xsize, content_ysize;
7126 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7128 if (element == EL_YAMYAM)
7130 num_contents = level->num_yamyam_contents;
7134 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7135 for (y = 0; y < 3; y++)
7136 for (x = 0; x < 3; x++)
7137 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7139 else if (element == EL_BD_AMOEBA)
7145 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7146 for (y = 0; y < 3; y++)
7147 for (x = 0; x < 3; x++)
7148 content_array[i][x][y] = EL_EMPTY;
7149 content_array[0][0][0] = level->amoeba_content;
7153 // chunk header already written -- write empty chunk data
7154 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7156 Warn("cannot save content for element '%d'", element);
7161 putFile16BitBE(file, element);
7162 putFile8Bit(file, num_contents);
7163 putFile8Bit(file, content_xsize);
7164 putFile8Bit(file, content_ysize);
7166 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7168 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7169 for (y = 0; y < 3; y++)
7170 for (x = 0; x < 3; x++)
7171 putFile16BitBE(file, content_array[i][x][y]);
7175 #if ENABLE_HISTORIC_CHUNKS
7176 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7178 int envelope_nr = element - EL_ENVELOPE_1;
7179 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7183 chunk_size += putFile16BitBE(file, element);
7184 chunk_size += putFile16BitBE(file, envelope_len);
7185 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7186 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7188 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7189 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7191 for (i = 0; i < envelope_len; i++)
7192 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7198 #if ENABLE_HISTORIC_CHUNKS
7199 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7200 int num_changed_custom_elements)
7204 putFile16BitBE(file, num_changed_custom_elements);
7206 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7208 int element = EL_CUSTOM_START + i;
7210 struct ElementInfo *ei = &element_info[element];
7212 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7214 if (check < num_changed_custom_elements)
7216 putFile16BitBE(file, element);
7217 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7224 if (check != num_changed_custom_elements) // should not happen
7225 Warn("inconsistent number of custom element properties");
7229 #if ENABLE_HISTORIC_CHUNKS
7230 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7231 int num_changed_custom_elements)
7235 putFile16BitBE(file, num_changed_custom_elements);
7237 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7239 int element = EL_CUSTOM_START + i;
7241 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7243 if (check < num_changed_custom_elements)
7245 putFile16BitBE(file, element);
7246 putFile16BitBE(file, element_info[element].change->target_element);
7253 if (check != num_changed_custom_elements) // should not happen
7254 Warn("inconsistent number of custom target elements");
7258 #if ENABLE_HISTORIC_CHUNKS
7259 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7260 int num_changed_custom_elements)
7262 int i, j, x, y, check = 0;
7264 putFile16BitBE(file, num_changed_custom_elements);
7266 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7268 int element = EL_CUSTOM_START + i;
7269 struct ElementInfo *ei = &element_info[element];
7271 if (ei->modified_settings)
7273 if (check < num_changed_custom_elements)
7275 putFile16BitBE(file, element);
7277 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7278 putFile8Bit(file, ei->description[j]);
7280 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7282 // some free bytes for future properties and padding
7283 WriteUnusedBytesToFile(file, 7);
7285 putFile8Bit(file, ei->use_gfx_element);
7286 putFile16BitBE(file, ei->gfx_element_initial);
7288 putFile8Bit(file, ei->collect_score_initial);
7289 putFile8Bit(file, ei->collect_count_initial);
7291 putFile16BitBE(file, ei->push_delay_fixed);
7292 putFile16BitBE(file, ei->push_delay_random);
7293 putFile16BitBE(file, ei->move_delay_fixed);
7294 putFile16BitBE(file, ei->move_delay_random);
7296 putFile16BitBE(file, ei->move_pattern);
7297 putFile8Bit(file, ei->move_direction_initial);
7298 putFile8Bit(file, ei->move_stepsize);
7300 for (y = 0; y < 3; y++)
7301 for (x = 0; x < 3; x++)
7302 putFile16BitBE(file, ei->content.e[x][y]);
7304 putFile32BitBE(file, ei->change->events);
7306 putFile16BitBE(file, ei->change->target_element);
7308 putFile16BitBE(file, ei->change->delay_fixed);
7309 putFile16BitBE(file, ei->change->delay_random);
7310 putFile16BitBE(file, ei->change->delay_frames);
7312 putFile16BitBE(file, ei->change->initial_trigger_element);
7314 putFile8Bit(file, ei->change->explode);
7315 putFile8Bit(file, ei->change->use_target_content);
7316 putFile8Bit(file, ei->change->only_if_complete);
7317 putFile8Bit(file, ei->change->use_random_replace);
7319 putFile8Bit(file, ei->change->random_percentage);
7320 putFile8Bit(file, ei->change->replace_when);
7322 for (y = 0; y < 3; y++)
7323 for (x = 0; x < 3; x++)
7324 putFile16BitBE(file, ei->change->content.e[x][y]);
7326 putFile8Bit(file, ei->slippery_type);
7328 // some free bytes for future properties and padding
7329 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7336 if (check != num_changed_custom_elements) // should not happen
7337 Warn("inconsistent number of custom element properties");
7341 #if ENABLE_HISTORIC_CHUNKS
7342 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7344 struct ElementInfo *ei = &element_info[element];
7347 // ---------- custom element base property values (96 bytes) ----------------
7349 putFile16BitBE(file, element);
7351 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7352 putFile8Bit(file, ei->description[i]);
7354 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7356 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7358 putFile8Bit(file, ei->num_change_pages);
7360 putFile16BitBE(file, ei->ce_value_fixed_initial);
7361 putFile16BitBE(file, ei->ce_value_random_initial);
7362 putFile8Bit(file, ei->use_last_ce_value);
7364 putFile8Bit(file, ei->use_gfx_element);
7365 putFile16BitBE(file, ei->gfx_element_initial);
7367 putFile8Bit(file, ei->collect_score_initial);
7368 putFile8Bit(file, ei->collect_count_initial);
7370 putFile8Bit(file, ei->drop_delay_fixed);
7371 putFile8Bit(file, ei->push_delay_fixed);
7372 putFile8Bit(file, ei->drop_delay_random);
7373 putFile8Bit(file, ei->push_delay_random);
7374 putFile16BitBE(file, ei->move_delay_fixed);
7375 putFile16BitBE(file, ei->move_delay_random);
7377 // bits 0 - 15 of "move_pattern" ...
7378 putFile16BitBE(file, ei->move_pattern & 0xffff);
7379 putFile8Bit(file, ei->move_direction_initial);
7380 putFile8Bit(file, ei->move_stepsize);
7382 putFile8Bit(file, ei->slippery_type);
7384 for (y = 0; y < 3; y++)
7385 for (x = 0; x < 3; x++)
7386 putFile16BitBE(file, ei->content.e[x][y]);
7388 putFile16BitBE(file, ei->move_enter_element);
7389 putFile16BitBE(file, ei->move_leave_element);
7390 putFile8Bit(file, ei->move_leave_type);
7392 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7393 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7395 putFile8Bit(file, ei->access_direction);
7397 putFile8Bit(file, ei->explosion_delay);
7398 putFile8Bit(file, ei->ignition_delay);
7399 putFile8Bit(file, ei->explosion_type);
7401 // some free bytes for future custom property values and padding
7402 WriteUnusedBytesToFile(file, 1);
7404 // ---------- change page property values (48 bytes) ------------------------
7406 for (i = 0; i < ei->num_change_pages; i++)
7408 struct ElementChangeInfo *change = &ei->change_page[i];
7409 unsigned int event_bits;
7411 // bits 0 - 31 of "has_event[]" ...
7413 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7414 if (change->has_event[j])
7415 event_bits |= (1u << j);
7416 putFile32BitBE(file, event_bits);
7418 putFile16BitBE(file, change->target_element);
7420 putFile16BitBE(file, change->delay_fixed);
7421 putFile16BitBE(file, change->delay_random);
7422 putFile16BitBE(file, change->delay_frames);
7424 putFile16BitBE(file, change->initial_trigger_element);
7426 putFile8Bit(file, change->explode);
7427 putFile8Bit(file, change->use_target_content);
7428 putFile8Bit(file, change->only_if_complete);
7429 putFile8Bit(file, change->use_random_replace);
7431 putFile8Bit(file, change->random_percentage);
7432 putFile8Bit(file, change->replace_when);
7434 for (y = 0; y < 3; y++)
7435 for (x = 0; x < 3; x++)
7436 putFile16BitBE(file, change->target_content.e[x][y]);
7438 putFile8Bit(file, change->can_change);
7440 putFile8Bit(file, change->trigger_side);
7442 putFile8Bit(file, change->trigger_player);
7443 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7444 log_2(change->trigger_page)));
7446 putFile8Bit(file, change->has_action);
7447 putFile8Bit(file, change->action_type);
7448 putFile8Bit(file, change->action_mode);
7449 putFile16BitBE(file, change->action_arg);
7451 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7453 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7454 if (change->has_event[j])
7455 event_bits |= (1u << (j - 32));
7456 putFile8Bit(file, event_bits);
7461 #if ENABLE_HISTORIC_CHUNKS
7462 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7464 struct ElementInfo *ei = &element_info[element];
7465 struct ElementGroupInfo *group = ei->group;
7468 putFile16BitBE(file, element);
7470 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7471 putFile8Bit(file, ei->description[i]);
7473 putFile8Bit(file, group->num_elements);
7475 putFile8Bit(file, ei->use_gfx_element);
7476 putFile16BitBE(file, ei->gfx_element_initial);
7478 putFile8Bit(file, group->choice_mode);
7480 // some free bytes for future values and padding
7481 WriteUnusedBytesToFile(file, 3);
7483 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7484 putFile16BitBE(file, group->element[i]);
7488 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7489 boolean write_element)
7491 int save_type = entry->save_type;
7492 int data_type = entry->data_type;
7493 int conf_type = entry->conf_type;
7494 int byte_mask = conf_type & CONF_MASK_BYTES;
7495 int element = entry->element;
7496 int default_value = entry->default_value;
7498 boolean modified = FALSE;
7500 if (byte_mask != CONF_MASK_MULTI_BYTES)
7502 void *value_ptr = entry->value;
7503 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7506 // check if any settings have been modified before saving them
7507 if (value != default_value)
7510 // do not save if explicitly told or if unmodified default settings
7511 if ((save_type == SAVE_CONF_NEVER) ||
7512 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7516 num_bytes += putFile16BitBE(file, element);
7518 num_bytes += putFile8Bit(file, conf_type);
7519 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7520 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7521 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7524 else if (data_type == TYPE_STRING)
7526 char *default_string = entry->default_string;
7527 char *string = (char *)(entry->value);
7528 int string_length = strlen(string);
7531 // check if any settings have been modified before saving them
7532 if (!strEqual(string, default_string))
7535 // do not save if explicitly told or if unmodified default settings
7536 if ((save_type == SAVE_CONF_NEVER) ||
7537 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7541 num_bytes += putFile16BitBE(file, element);
7543 num_bytes += putFile8Bit(file, conf_type);
7544 num_bytes += putFile16BitBE(file, string_length);
7546 for (i = 0; i < string_length; i++)
7547 num_bytes += putFile8Bit(file, string[i]);
7549 else if (data_type == TYPE_ELEMENT_LIST)
7551 int *element_array = (int *)(entry->value);
7552 int num_elements = *(int *)(entry->num_entities);
7555 // check if any settings have been modified before saving them
7556 for (i = 0; i < num_elements; i++)
7557 if (element_array[i] != default_value)
7560 // do not save if explicitly told or if unmodified default settings
7561 if ((save_type == SAVE_CONF_NEVER) ||
7562 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7566 num_bytes += putFile16BitBE(file, element);
7568 num_bytes += putFile8Bit(file, conf_type);
7569 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7571 for (i = 0; i < num_elements; i++)
7572 num_bytes += putFile16BitBE(file, element_array[i]);
7574 else if (data_type == TYPE_CONTENT_LIST)
7576 struct Content *content = (struct Content *)(entry->value);
7577 int num_contents = *(int *)(entry->num_entities);
7580 // check if any settings have been modified before saving them
7581 for (i = 0; i < num_contents; i++)
7582 for (y = 0; y < 3; y++)
7583 for (x = 0; x < 3; x++)
7584 if (content[i].e[x][y] != default_value)
7587 // do not save if explicitly told or if unmodified default settings
7588 if ((save_type == SAVE_CONF_NEVER) ||
7589 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7593 num_bytes += putFile16BitBE(file, element);
7595 num_bytes += putFile8Bit(file, conf_type);
7596 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7598 for (i = 0; i < num_contents; i++)
7599 for (y = 0; y < 3; y++)
7600 for (x = 0; x < 3; x++)
7601 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7607 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7612 li = *level; // copy level data into temporary buffer
7614 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7615 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7620 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7625 li = *level; // copy level data into temporary buffer
7627 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7628 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7633 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7635 int envelope_nr = element - EL_ENVELOPE_1;
7639 chunk_size += putFile16BitBE(file, element);
7641 // copy envelope data into temporary buffer
7642 xx_envelope = level->envelope[envelope_nr];
7644 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7645 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7650 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7652 struct ElementInfo *ei = &element_info[element];
7656 chunk_size += putFile16BitBE(file, element);
7658 xx_ei = *ei; // copy element data into temporary buffer
7660 // set default description string for this specific element
7661 strcpy(xx_default_description, getDefaultElementDescription(ei));
7663 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7664 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7666 for (i = 0; i < ei->num_change_pages; i++)
7668 struct ElementChangeInfo *change = &ei->change_page[i];
7670 xx_current_change_page = i;
7672 xx_change = *change; // copy change data into temporary buffer
7675 setEventBitsFromEventFlags(change);
7677 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7678 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7685 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7687 struct ElementInfo *ei = &element_info[element];
7688 struct ElementGroupInfo *group = ei->group;
7692 chunk_size += putFile16BitBE(file, element);
7694 xx_ei = *ei; // copy element data into temporary buffer
7695 xx_group = *group; // copy group data into temporary buffer
7697 // set default description string for this specific element
7698 strcpy(xx_default_description, getDefaultElementDescription(ei));
7700 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7701 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7706 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7708 struct ElementInfo *ei = &element_info[element];
7712 chunk_size += putFile16BitBE(file, element);
7714 xx_ei = *ei; // copy element data into temporary buffer
7716 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7717 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7722 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7723 boolean save_as_template)
7729 if (!(file = fopen(filename, MODE_WRITE)))
7731 Warn("cannot save level file '%s'", filename);
7736 level->file_version = FILE_VERSION_ACTUAL;
7737 level->game_version = GAME_VERSION_ACTUAL;
7739 level->creation_date = getCurrentDate();
7741 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7742 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7744 chunk_size = SaveLevel_VERS(NULL, level);
7745 putFileChunkBE(file, "VERS", chunk_size);
7746 SaveLevel_VERS(file, level);
7748 chunk_size = SaveLevel_DATE(NULL, level);
7749 putFileChunkBE(file, "DATE", chunk_size);
7750 SaveLevel_DATE(file, level);
7752 chunk_size = SaveLevel_NAME(NULL, level);
7753 putFileChunkBE(file, "NAME", chunk_size);
7754 SaveLevel_NAME(file, level);
7756 chunk_size = SaveLevel_AUTH(NULL, level);
7757 putFileChunkBE(file, "AUTH", chunk_size);
7758 SaveLevel_AUTH(file, level);
7760 chunk_size = SaveLevel_INFO(NULL, level);
7761 putFileChunkBE(file, "INFO", chunk_size);
7762 SaveLevel_INFO(file, level);
7764 chunk_size = SaveLevel_BODY(NULL, level);
7765 putFileChunkBE(file, "BODY", chunk_size);
7766 SaveLevel_BODY(file, level);
7768 chunk_size = SaveLevel_ELEM(NULL, level);
7769 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7771 putFileChunkBE(file, "ELEM", chunk_size);
7772 SaveLevel_ELEM(file, level);
7775 for (i = 0; i < NUM_ENVELOPES; i++)
7777 int element = EL_ENVELOPE_1 + i;
7779 chunk_size = SaveLevel_NOTE(NULL, level, element);
7780 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7782 putFileChunkBE(file, "NOTE", chunk_size);
7783 SaveLevel_NOTE(file, level, element);
7787 // if not using template level, check for non-default custom/group elements
7788 if (!level->use_custom_template || save_as_template)
7790 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7792 int element = EL_CUSTOM_START + i;
7794 chunk_size = SaveLevel_CUSX(NULL, level, element);
7795 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7797 putFileChunkBE(file, "CUSX", chunk_size);
7798 SaveLevel_CUSX(file, level, element);
7802 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7804 int element = EL_GROUP_START + i;
7806 chunk_size = SaveLevel_GRPX(NULL, level, element);
7807 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7809 putFileChunkBE(file, "GRPX", chunk_size);
7810 SaveLevel_GRPX(file, level, element);
7814 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7816 int element = GET_EMPTY_ELEMENT(i);
7818 chunk_size = SaveLevel_EMPX(NULL, level, element);
7819 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7821 putFileChunkBE(file, "EMPX", chunk_size);
7822 SaveLevel_EMPX(file, level, element);
7829 SetFilePermissions(filename, PERMS_PRIVATE);
7832 void SaveLevel(int nr)
7834 char *filename = getDefaultLevelFilename(nr);
7836 SaveLevelFromFilename(&level, filename, FALSE);
7839 void SaveLevelTemplate(void)
7841 char *filename = getLocalLevelTemplateFilename();
7843 SaveLevelFromFilename(&level, filename, TRUE);
7846 boolean SaveLevelChecked(int nr)
7848 char *filename = getDefaultLevelFilename(nr);
7849 boolean new_level = !fileExists(filename);
7850 boolean level_saved = FALSE;
7852 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7857 Request("Level saved!", REQ_CONFIRM);
7865 void DumpLevel(struct LevelInfo *level)
7867 if (level->no_level_file || level->no_valid_file)
7869 Warn("cannot dump -- no valid level file found");
7875 Print("Level xxx (file version %08d, game version %08d)\n",
7876 level->file_version, level->game_version);
7879 Print("Level author: '%s'\n", level->author);
7880 Print("Level title: '%s'\n", level->name);
7882 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7884 Print("Level time: %d seconds\n", level->time);
7885 Print("Gems needed: %d\n", level->gems_needed);
7887 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7888 Print("Time for wheel: %d seconds\n", level->time_wheel);
7889 Print("Time for light: %d seconds\n", level->time_light);
7890 Print("Time for timegate: %d seconds\n", level->time_timegate);
7892 Print("Amoeba speed: %d\n", level->amoeba_speed);
7895 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7896 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7897 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7898 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7899 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7900 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7906 for (i = 0; i < NUM_ENVELOPES; i++)
7908 char *text = level->envelope[i].text;
7909 int text_len = strlen(text);
7910 boolean has_text = FALSE;
7912 for (j = 0; j < text_len; j++)
7913 if (text[j] != ' ' && text[j] != '\n')
7919 Print("Envelope %d:\n'%s'\n", i + 1, text);
7927 void DumpLevels(void)
7929 static LevelDirTree *dumplevel_leveldir = NULL;
7931 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7932 global.dumplevel_leveldir);
7934 if (dumplevel_leveldir == NULL)
7935 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7937 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7938 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7939 Fail("no such level number: %d", global.dumplevel_level_nr);
7941 leveldir_current = dumplevel_leveldir;
7943 LoadLevel(global.dumplevel_level_nr);
7950 // ============================================================================
7951 // tape file functions
7952 // ============================================================================
7954 static void setTapeInfoToDefaults(void)
7958 // always start with reliable default values (empty tape)
7961 // default values (also for pre-1.2 tapes) with only the first player
7962 tape.player_participates[0] = TRUE;
7963 for (i = 1; i < MAX_PLAYERS; i++)
7964 tape.player_participates[i] = FALSE;
7966 // at least one (default: the first) player participates in every tape
7967 tape.num_participating_players = 1;
7969 tape.property_bits = TAPE_PROPERTY_NONE;
7971 tape.level_nr = level_nr;
7973 tape.changed = FALSE;
7974 tape.solved = FALSE;
7976 tape.recording = FALSE;
7977 tape.playing = FALSE;
7978 tape.pausing = FALSE;
7980 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7981 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7983 tape.no_info_chunk = TRUE;
7984 tape.no_valid_file = FALSE;
7987 static int getTapePosSize(struct TapeInfo *tape)
7989 int tape_pos_size = 0;
7991 if (tape->use_key_actions)
7992 tape_pos_size += tape->num_participating_players;
7994 if (tape->use_mouse_actions)
7995 tape_pos_size += 3; // x and y position and mouse button mask
7997 tape_pos_size += 1; // tape action delay value
7999 return tape_pos_size;
8002 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8004 tape->use_key_actions = FALSE;
8005 tape->use_mouse_actions = FALSE;
8007 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8008 tape->use_key_actions = TRUE;
8010 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8011 tape->use_mouse_actions = TRUE;
8014 static int getTapeActionValue(struct TapeInfo *tape)
8016 return (tape->use_key_actions &&
8017 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8018 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8019 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8020 TAPE_ACTIONS_DEFAULT);
8023 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8025 tape->file_version = getFileVersion(file);
8026 tape->game_version = getFileVersion(file);
8031 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8035 tape->random_seed = getFile32BitBE(file);
8036 tape->date = getFile32BitBE(file);
8037 tape->length = getFile32BitBE(file);
8039 // read header fields that are new since version 1.2
8040 if (tape->file_version >= FILE_VERSION_1_2)
8042 byte store_participating_players = getFile8Bit(file);
8045 // since version 1.2, tapes store which players participate in the tape
8046 tape->num_participating_players = 0;
8047 for (i = 0; i < MAX_PLAYERS; i++)
8049 tape->player_participates[i] = FALSE;
8051 if (store_participating_players & (1 << i))
8053 tape->player_participates[i] = TRUE;
8054 tape->num_participating_players++;
8058 setTapeActionFlags(tape, getFile8Bit(file));
8060 tape->property_bits = getFile8Bit(file);
8061 tape->solved = getFile8Bit(file);
8063 engine_version = getFileVersion(file);
8064 if (engine_version > 0)
8065 tape->engine_version = engine_version;
8067 tape->engine_version = tape->game_version;
8073 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8075 tape->scr_fieldx = getFile8Bit(file);
8076 tape->scr_fieldy = getFile8Bit(file);
8081 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8083 char *level_identifier = NULL;
8084 int level_identifier_size;
8087 tape->no_info_chunk = FALSE;
8089 level_identifier_size = getFile16BitBE(file);
8091 level_identifier = checked_malloc(level_identifier_size);
8093 for (i = 0; i < level_identifier_size; i++)
8094 level_identifier[i] = getFile8Bit(file);
8096 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8097 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8099 checked_free(level_identifier);
8101 tape->level_nr = getFile16BitBE(file);
8103 chunk_size = 2 + level_identifier_size + 2;
8108 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8111 int tape_pos_size = getTapePosSize(tape);
8112 int chunk_size_expected = tape_pos_size * tape->length;
8114 if (chunk_size_expected != chunk_size)
8116 ReadUnusedBytesFromFile(file, chunk_size);
8117 return chunk_size_expected;
8120 for (i = 0; i < tape->length; i++)
8122 if (i >= MAX_TAPE_LEN)
8124 Warn("tape truncated -- size exceeds maximum tape size %d",
8127 // tape too large; read and ignore remaining tape data from this chunk
8128 for (;i < tape->length; i++)
8129 ReadUnusedBytesFromFile(file, tape_pos_size);
8134 if (tape->use_key_actions)
8136 for (j = 0; j < MAX_PLAYERS; j++)
8138 tape->pos[i].action[j] = MV_NONE;
8140 if (tape->player_participates[j])
8141 tape->pos[i].action[j] = getFile8Bit(file);
8145 if (tape->use_mouse_actions)
8147 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8148 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8149 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8152 tape->pos[i].delay = getFile8Bit(file);
8154 if (tape->file_version == FILE_VERSION_1_0)
8156 // eliminate possible diagonal moves in old tapes
8157 // this is only for backward compatibility
8159 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8160 byte action = tape->pos[i].action[0];
8161 int k, num_moves = 0;
8163 for (k = 0; k < 4; k++)
8165 if (action & joy_dir[k])
8167 tape->pos[i + num_moves].action[0] = joy_dir[k];
8169 tape->pos[i + num_moves].delay = 0;
8178 tape->length += num_moves;
8181 else if (tape->file_version < FILE_VERSION_2_0)
8183 // convert pre-2.0 tapes to new tape format
8185 if (tape->pos[i].delay > 1)
8188 tape->pos[i + 1] = tape->pos[i];
8189 tape->pos[i + 1].delay = 1;
8192 for (j = 0; j < MAX_PLAYERS; j++)
8193 tape->pos[i].action[j] = MV_NONE;
8194 tape->pos[i].delay--;
8201 if (checkEndOfFile(file))
8205 if (i != tape->length)
8206 chunk_size = tape_pos_size * i;
8211 static void LoadTape_SokobanSolution(char *filename)
8214 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8216 if (!(file = openFile(filename, MODE_READ)))
8218 tape.no_valid_file = TRUE;
8223 while (!checkEndOfFile(file))
8225 unsigned char c = getByteFromFile(file);
8227 if (checkEndOfFile(file))
8234 tape.pos[tape.length].action[0] = MV_UP;
8235 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8241 tape.pos[tape.length].action[0] = MV_DOWN;
8242 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8248 tape.pos[tape.length].action[0] = MV_LEFT;
8249 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8255 tape.pos[tape.length].action[0] = MV_RIGHT;
8256 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8264 // ignore white-space characters
8268 tape.no_valid_file = TRUE;
8270 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8278 if (tape.no_valid_file)
8281 tape.length_frames = GetTapeLengthFrames();
8282 tape.length_seconds = GetTapeLengthSeconds();
8285 void LoadTapeFromFilename(char *filename)
8287 char cookie[MAX_LINE_LEN];
8288 char chunk_name[CHUNK_ID_LEN + 1];
8292 // always start with reliable default values
8293 setTapeInfoToDefaults();
8295 if (strSuffix(filename, ".sln"))
8297 LoadTape_SokobanSolution(filename);
8302 if (!(file = openFile(filename, MODE_READ)))
8304 tape.no_valid_file = TRUE;
8309 getFileChunkBE(file, chunk_name, NULL);
8310 if (strEqual(chunk_name, "RND1"))
8312 getFile32BitBE(file); // not used
8314 getFileChunkBE(file, chunk_name, NULL);
8315 if (!strEqual(chunk_name, "TAPE"))
8317 tape.no_valid_file = TRUE;
8319 Warn("unknown format of tape file '%s'", filename);
8326 else // check for pre-2.0 file format with cookie string
8328 strcpy(cookie, chunk_name);
8329 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8331 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8332 cookie[strlen(cookie) - 1] = '\0';
8334 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8336 tape.no_valid_file = TRUE;
8338 Warn("unknown format of tape file '%s'", filename);
8345 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8347 tape.no_valid_file = TRUE;
8349 Warn("unsupported version of tape file '%s'", filename);
8356 // pre-2.0 tape files have no game version, so use file version here
8357 tape.game_version = tape.file_version;
8360 if (tape.file_version < FILE_VERSION_1_2)
8362 // tape files from versions before 1.2.0 without chunk structure
8363 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8364 LoadTape_BODY(file, 2 * tape.length, &tape);
8372 int (*loader)(File *, int, struct TapeInfo *);
8376 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8377 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8378 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8379 { "INFO", -1, LoadTape_INFO },
8380 { "BODY", -1, LoadTape_BODY },
8384 while (getFileChunkBE(file, chunk_name, &chunk_size))
8388 while (chunk_info[i].name != NULL &&
8389 !strEqual(chunk_name, chunk_info[i].name))
8392 if (chunk_info[i].name == NULL)
8394 Warn("unknown chunk '%s' in tape file '%s'",
8395 chunk_name, filename);
8397 ReadUnusedBytesFromFile(file, chunk_size);
8399 else if (chunk_info[i].size != -1 &&
8400 chunk_info[i].size != chunk_size)
8402 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8403 chunk_size, chunk_name, filename);
8405 ReadUnusedBytesFromFile(file, chunk_size);
8409 // call function to load this tape chunk
8410 int chunk_size_expected =
8411 (chunk_info[i].loader)(file, chunk_size, &tape);
8413 // the size of some chunks cannot be checked before reading other
8414 // chunks first (like "HEAD" and "BODY") that contain some header
8415 // information, so check them here
8416 if (chunk_size_expected != chunk_size)
8418 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8419 chunk_size, chunk_name, filename);
8427 tape.length_frames = GetTapeLengthFrames();
8428 tape.length_seconds = GetTapeLengthSeconds();
8431 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8433 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8435 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8436 tape.engine_version);
8440 void LoadTape(int nr)
8442 char *filename = getTapeFilename(nr);
8444 LoadTapeFromFilename(filename);
8447 void LoadSolutionTape(int nr)
8449 char *filename = getSolutionTapeFilename(nr);
8451 LoadTapeFromFilename(filename);
8453 if (TAPE_IS_EMPTY(tape) &&
8454 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8455 level.native_sp_level->demo.is_available)
8456 CopyNativeTape_SP_to_RND(&level);
8459 void LoadScoreTape(char *score_tape_basename, int nr)
8461 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8463 LoadTapeFromFilename(filename);
8466 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8468 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8470 LoadTapeFromFilename(filename);
8473 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8475 // chunk required for team mode tapes with non-default screen size
8476 return (tape->num_participating_players > 1 &&
8477 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8478 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8481 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8483 putFileVersion(file, tape->file_version);
8484 putFileVersion(file, tape->game_version);
8487 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8490 byte store_participating_players = 0;
8492 // set bits for participating players for compact storage
8493 for (i = 0; i < MAX_PLAYERS; i++)
8494 if (tape->player_participates[i])
8495 store_participating_players |= (1 << i);
8497 putFile32BitBE(file, tape->random_seed);
8498 putFile32BitBE(file, tape->date);
8499 putFile32BitBE(file, tape->length);
8501 putFile8Bit(file, store_participating_players);
8503 putFile8Bit(file, getTapeActionValue(tape));
8505 putFile8Bit(file, tape->property_bits);
8506 putFile8Bit(file, tape->solved);
8508 putFileVersion(file, tape->engine_version);
8511 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8513 putFile8Bit(file, tape->scr_fieldx);
8514 putFile8Bit(file, tape->scr_fieldy);
8517 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8519 int level_identifier_size = strlen(tape->level_identifier) + 1;
8522 putFile16BitBE(file, level_identifier_size);
8524 for (i = 0; i < level_identifier_size; i++)
8525 putFile8Bit(file, tape->level_identifier[i]);
8527 putFile16BitBE(file, tape->level_nr);
8530 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8534 for (i = 0; i < tape->length; i++)
8536 if (tape->use_key_actions)
8538 for (j = 0; j < MAX_PLAYERS; j++)
8539 if (tape->player_participates[j])
8540 putFile8Bit(file, tape->pos[i].action[j]);
8543 if (tape->use_mouse_actions)
8545 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8546 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8547 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8550 putFile8Bit(file, tape->pos[i].delay);
8554 void SaveTapeToFilename(char *filename)
8558 int info_chunk_size;
8559 int body_chunk_size;
8561 if (!(file = fopen(filename, MODE_WRITE)))
8563 Warn("cannot save level recording file '%s'", filename);
8568 tape_pos_size = getTapePosSize(&tape);
8570 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8571 body_chunk_size = tape_pos_size * tape.length;
8573 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8574 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8576 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8577 SaveTape_VERS(file, &tape);
8579 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8580 SaveTape_HEAD(file, &tape);
8582 if (checkSaveTape_SCRN(&tape))
8584 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8585 SaveTape_SCRN(file, &tape);
8588 putFileChunkBE(file, "INFO", info_chunk_size);
8589 SaveTape_INFO(file, &tape);
8591 putFileChunkBE(file, "BODY", body_chunk_size);
8592 SaveTape_BODY(file, &tape);
8596 SetFilePermissions(filename, PERMS_PRIVATE);
8599 static void SaveTapeExt(char *filename)
8603 tape.file_version = FILE_VERSION_ACTUAL;
8604 tape.game_version = GAME_VERSION_ACTUAL;
8606 tape.num_participating_players = 0;
8608 // count number of participating players
8609 for (i = 0; i < MAX_PLAYERS; i++)
8610 if (tape.player_participates[i])
8611 tape.num_participating_players++;
8613 SaveTapeToFilename(filename);
8615 tape.changed = FALSE;
8618 void SaveTape(int nr)
8620 char *filename = getTapeFilename(nr);
8622 InitTapeDirectory(leveldir_current->subdir);
8624 SaveTapeExt(filename);
8627 void SaveScoreTape(int nr)
8629 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8631 // used instead of "leveldir_current->subdir" (for network games)
8632 InitScoreTapeDirectory(levelset.identifier, nr);
8634 SaveTapeExt(filename);
8637 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8638 unsigned int req_state_added)
8640 char *filename = getTapeFilename(nr);
8641 boolean new_tape = !fileExists(filename);
8642 boolean tape_saved = FALSE;
8644 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8649 Request(msg_saved, REQ_CONFIRM | req_state_added);
8657 boolean SaveTapeChecked(int nr)
8659 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8662 boolean SaveTapeChecked_LevelSolved(int nr)
8664 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8665 "Level solved! Tape saved!", REQ_STAY_OPEN);
8668 void DumpTape(struct TapeInfo *tape)
8670 int tape_frame_counter;
8673 if (tape->no_valid_file)
8675 Warn("cannot dump -- no valid tape file found");
8682 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8683 tape->level_nr, tape->file_version, tape->game_version);
8684 Print(" (effective engine version %08d)\n",
8685 tape->engine_version);
8686 Print("Level series identifier: '%s'\n", tape->level_identifier);
8688 Print("Solution tape: %s\n",
8689 tape->solved ? "yes" :
8690 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8692 Print("Special tape properties: ");
8693 if (tape->property_bits == TAPE_PROPERTY_NONE)
8695 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8696 Print("[em_random_bug]");
8697 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8698 Print("[game_speed]");
8699 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8701 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8702 Print("[single_step]");
8703 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8704 Print("[snapshot]");
8705 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8706 Print("[replayed]");
8707 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8708 Print("[tas_keys]");
8709 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8710 Print("[small_graphics]");
8713 int year2 = tape->date / 10000;
8714 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8715 int month_index_raw = (tape->date / 100) % 100;
8716 int month_index = month_index_raw % 12; // prevent invalid index
8717 int month = month_index + 1;
8718 int day = tape->date % 100;
8720 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8724 tape_frame_counter = 0;
8726 for (i = 0; i < tape->length; i++)
8728 if (i >= MAX_TAPE_LEN)
8733 for (j = 0; j < MAX_PLAYERS; j++)
8735 if (tape->player_participates[j])
8737 int action = tape->pos[i].action[j];
8739 Print("%d:%02x ", j, action);
8740 Print("[%c%c%c%c|%c%c] - ",
8741 (action & JOY_LEFT ? '<' : ' '),
8742 (action & JOY_RIGHT ? '>' : ' '),
8743 (action & JOY_UP ? '^' : ' '),
8744 (action & JOY_DOWN ? 'v' : ' '),
8745 (action & JOY_BUTTON_1 ? '1' : ' '),
8746 (action & JOY_BUTTON_2 ? '2' : ' '));
8750 Print("(%03d) ", tape->pos[i].delay);
8751 Print("[%05d]\n", tape_frame_counter);
8753 tape_frame_counter += tape->pos[i].delay;
8759 void DumpTapes(void)
8761 static LevelDirTree *dumptape_leveldir = NULL;
8763 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8764 global.dumptape_leveldir);
8766 if (dumptape_leveldir == NULL)
8767 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8769 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8770 global.dumptape_level_nr > dumptape_leveldir->last_level)
8771 Fail("no such level number: %d", global.dumptape_level_nr);
8773 leveldir_current = dumptape_leveldir;
8775 if (options.mytapes)
8776 LoadTape(global.dumptape_level_nr);
8778 LoadSolutionTape(global.dumptape_level_nr);
8786 // ============================================================================
8787 // score file functions
8788 // ============================================================================
8790 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8794 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8796 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8797 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8798 scores->entry[i].score = 0;
8799 scores->entry[i].time = 0;
8801 scores->entry[i].id = -1;
8802 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8803 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8804 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8805 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8806 strcpy(scores->entry[i].country_code, "??");
8809 scores->num_entries = 0;
8810 scores->last_added = -1;
8811 scores->last_added_local = -1;
8813 scores->updated = FALSE;
8814 scores->uploaded = FALSE;
8815 scores->tape_downloaded = FALSE;
8816 scores->force_last_added = FALSE;
8818 // The following values are intentionally not reset here:
8822 // - continue_playing
8823 // - continue_on_return
8826 static void setScoreInfoToDefaults(void)
8828 setScoreInfoToDefaultsExt(&scores);
8831 static void setServerScoreInfoToDefaults(void)
8833 setScoreInfoToDefaultsExt(&server_scores);
8836 static void LoadScore_OLD(int nr)
8839 char *filename = getScoreFilename(nr);
8840 char cookie[MAX_LINE_LEN];
8841 char line[MAX_LINE_LEN];
8845 if (!(file = fopen(filename, MODE_READ)))
8848 // check file identifier
8849 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8851 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8852 cookie[strlen(cookie) - 1] = '\0';
8854 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8856 Warn("unknown format of score file '%s'", filename);
8863 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8865 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8866 Warn("fscanf() failed; %s", strerror(errno));
8868 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8871 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8872 line[strlen(line) - 1] = '\0';
8874 for (line_ptr = line; *line_ptr; line_ptr++)
8876 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8878 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8879 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8888 static void ConvertScore_OLD(void)
8890 // only convert score to time for levels that rate playing time over score
8891 if (!level.rate_time_over_score)
8894 // convert old score to playing time for score-less levels (like Supaplex)
8895 int time_final_max = 999;
8898 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8900 int score = scores.entry[i].score;
8902 if (score > 0 && score < time_final_max)
8903 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8907 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8909 scores->file_version = getFileVersion(file);
8910 scores->game_version = getFileVersion(file);
8915 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8917 char *level_identifier = NULL;
8918 int level_identifier_size;
8921 level_identifier_size = getFile16BitBE(file);
8923 level_identifier = checked_malloc(level_identifier_size);
8925 for (i = 0; i < level_identifier_size; i++)
8926 level_identifier[i] = getFile8Bit(file);
8928 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8929 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8931 checked_free(level_identifier);
8933 scores->level_nr = getFile16BitBE(file);
8934 scores->num_entries = getFile16BitBE(file);
8936 chunk_size = 2 + level_identifier_size + 2 + 2;
8941 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8945 for (i = 0; i < scores->num_entries; i++)
8947 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8948 scores->entry[i].name[j] = getFile8Bit(file);
8950 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8953 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8958 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8962 for (i = 0; i < scores->num_entries; i++)
8963 scores->entry[i].score = getFile16BitBE(file);
8965 chunk_size = scores->num_entries * 2;
8970 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8974 for (i = 0; i < scores->num_entries; i++)
8975 scores->entry[i].score = getFile32BitBE(file);
8977 chunk_size = scores->num_entries * 4;
8982 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8986 for (i = 0; i < scores->num_entries; i++)
8987 scores->entry[i].time = getFile32BitBE(file);
8989 chunk_size = scores->num_entries * 4;
8994 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8998 for (i = 0; i < scores->num_entries; i++)
9000 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9001 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9003 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9006 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9011 void LoadScore(int nr)
9013 char *filename = getScoreFilename(nr);
9014 char cookie[MAX_LINE_LEN];
9015 char chunk_name[CHUNK_ID_LEN + 1];
9017 boolean old_score_file_format = FALSE;
9020 // always start with reliable default values
9021 setScoreInfoToDefaults();
9023 if (!(file = openFile(filename, MODE_READ)))
9026 getFileChunkBE(file, chunk_name, NULL);
9027 if (strEqual(chunk_name, "RND1"))
9029 getFile32BitBE(file); // not used
9031 getFileChunkBE(file, chunk_name, NULL);
9032 if (!strEqual(chunk_name, "SCOR"))
9034 Warn("unknown format of score file '%s'", filename);
9041 else // check for old file format with cookie string
9043 strcpy(cookie, chunk_name);
9044 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9046 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9047 cookie[strlen(cookie) - 1] = '\0';
9049 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9051 Warn("unknown format of score file '%s'", filename);
9058 old_score_file_format = TRUE;
9061 if (old_score_file_format)
9063 // score files from versions before 4.2.4.0 without chunk structure
9066 // convert score to time, if possible (mainly for Supaplex levels)
9075 int (*loader)(File *, int, struct ScoreInfo *);
9079 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9080 { "INFO", -1, LoadScore_INFO },
9081 { "NAME", -1, LoadScore_NAME },
9082 { "SCOR", -1, LoadScore_SCOR },
9083 { "SC4R", -1, LoadScore_SC4R },
9084 { "TIME", -1, LoadScore_TIME },
9085 { "TAPE", -1, LoadScore_TAPE },
9090 while (getFileChunkBE(file, chunk_name, &chunk_size))
9094 while (chunk_info[i].name != NULL &&
9095 !strEqual(chunk_name, chunk_info[i].name))
9098 if (chunk_info[i].name == NULL)
9100 Warn("unknown chunk '%s' in score file '%s'",
9101 chunk_name, filename);
9103 ReadUnusedBytesFromFile(file, chunk_size);
9105 else if (chunk_info[i].size != -1 &&
9106 chunk_info[i].size != chunk_size)
9108 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9109 chunk_size, chunk_name, filename);
9111 ReadUnusedBytesFromFile(file, chunk_size);
9115 // call function to load this score chunk
9116 int chunk_size_expected =
9117 (chunk_info[i].loader)(file, chunk_size, &scores);
9119 // the size of some chunks cannot be checked before reading other
9120 // chunks first (like "HEAD" and "BODY") that contain some header
9121 // information, so check them here
9122 if (chunk_size_expected != chunk_size)
9124 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9125 chunk_size, chunk_name, filename);
9134 #if ENABLE_HISTORIC_CHUNKS
9135 void SaveScore_OLD(int nr)
9138 char *filename = getScoreFilename(nr);
9141 // used instead of "leveldir_current->subdir" (for network games)
9142 InitScoreDirectory(levelset.identifier);
9144 if (!(file = fopen(filename, MODE_WRITE)))
9146 Warn("cannot save score for level %d", nr);
9151 fprintf(file, "%s\n\n", SCORE_COOKIE);
9153 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9154 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9158 SetFilePermissions(filename, PERMS_PRIVATE);
9162 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9164 putFileVersion(file, scores->file_version);
9165 putFileVersion(file, scores->game_version);
9168 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9170 int level_identifier_size = strlen(scores->level_identifier) + 1;
9173 putFile16BitBE(file, level_identifier_size);
9175 for (i = 0; i < level_identifier_size; i++)
9176 putFile8Bit(file, scores->level_identifier[i]);
9178 putFile16BitBE(file, scores->level_nr);
9179 putFile16BitBE(file, scores->num_entries);
9182 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9186 for (i = 0; i < scores->num_entries; i++)
9188 int name_size = strlen(scores->entry[i].name);
9190 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9191 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9195 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9199 for (i = 0; i < scores->num_entries; i++)
9200 putFile16BitBE(file, scores->entry[i].score);
9203 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9207 for (i = 0; i < scores->num_entries; i++)
9208 putFile32BitBE(file, scores->entry[i].score);
9211 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9215 for (i = 0; i < scores->num_entries; i++)
9216 putFile32BitBE(file, scores->entry[i].time);
9219 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9223 for (i = 0; i < scores->num_entries; i++)
9225 int size = strlen(scores->entry[i].tape_basename);
9227 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9228 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9232 static void SaveScoreToFilename(char *filename)
9235 int info_chunk_size;
9236 int name_chunk_size;
9237 int scor_chunk_size;
9238 int sc4r_chunk_size;
9239 int time_chunk_size;
9240 int tape_chunk_size;
9241 boolean has_large_score_values;
9244 if (!(file = fopen(filename, MODE_WRITE)))
9246 Warn("cannot save score file '%s'", filename);
9251 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9252 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9253 scor_chunk_size = scores.num_entries * 2;
9254 sc4r_chunk_size = scores.num_entries * 4;
9255 time_chunk_size = scores.num_entries * 4;
9256 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9258 has_large_score_values = FALSE;
9259 for (i = 0; i < scores.num_entries; i++)
9260 if (scores.entry[i].score > 0xffff)
9261 has_large_score_values = TRUE;
9263 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9264 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9266 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9267 SaveScore_VERS(file, &scores);
9269 putFileChunkBE(file, "INFO", info_chunk_size);
9270 SaveScore_INFO(file, &scores);
9272 putFileChunkBE(file, "NAME", name_chunk_size);
9273 SaveScore_NAME(file, &scores);
9275 if (has_large_score_values)
9277 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9278 SaveScore_SC4R(file, &scores);
9282 putFileChunkBE(file, "SCOR", scor_chunk_size);
9283 SaveScore_SCOR(file, &scores);
9286 putFileChunkBE(file, "TIME", time_chunk_size);
9287 SaveScore_TIME(file, &scores);
9289 putFileChunkBE(file, "TAPE", tape_chunk_size);
9290 SaveScore_TAPE(file, &scores);
9294 SetFilePermissions(filename, PERMS_PRIVATE);
9297 void SaveScore(int nr)
9299 char *filename = getScoreFilename(nr);
9302 // used instead of "leveldir_current->subdir" (for network games)
9303 InitScoreDirectory(levelset.identifier);
9305 scores.file_version = FILE_VERSION_ACTUAL;
9306 scores.game_version = GAME_VERSION_ACTUAL;
9308 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9309 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9310 scores.level_nr = level_nr;
9312 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9313 if (scores.entry[i].score == 0 &&
9314 scores.entry[i].time == 0 &&
9315 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9318 scores.num_entries = i;
9320 if (scores.num_entries == 0)
9323 SaveScoreToFilename(filename);
9326 static void LoadServerScoreFromCache(int nr)
9328 struct ScoreEntry score_entry;
9337 { &score_entry.score, FALSE, 0 },
9338 { &score_entry.time, FALSE, 0 },
9339 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9340 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9341 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9342 { &score_entry.id, FALSE, 0 },
9343 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9344 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9345 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9346 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9350 char *filename = getScoreCacheFilename(nr);
9351 SetupFileHash *score_hash = loadSetupFileHash(filename);
9354 server_scores.num_entries = 0;
9356 if (score_hash == NULL)
9359 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9361 score_entry = server_scores.entry[i];
9363 for (j = 0; score_mapping[j].value != NULL; j++)
9367 sprintf(token, "%02d.%d", i, j);
9369 char *value = getHashEntry(score_hash, token);
9374 if (score_mapping[j].is_string)
9376 char *score_value = (char *)score_mapping[j].value;
9377 int value_size = score_mapping[j].string_size;
9379 strncpy(score_value, value, value_size);
9380 score_value[value_size] = '\0';
9384 int *score_value = (int *)score_mapping[j].value;
9386 *score_value = atoi(value);
9389 server_scores.num_entries = i + 1;
9392 server_scores.entry[i] = score_entry;
9395 freeSetupFileHash(score_hash);
9398 void LoadServerScore(int nr, boolean download_score)
9400 if (!setup.use_api_server)
9403 // always start with reliable default values
9404 setServerScoreInfoToDefaults();
9406 // 1st step: load server scores from cache file (which may not exist)
9407 // (this should prevent reading it while the thread is writing to it)
9408 LoadServerScoreFromCache(nr);
9410 if (download_score && runtime.use_api_server)
9412 // 2nd step: download server scores from score server to cache file
9413 // (as thread, as it might time out if the server is not reachable)
9414 ApiGetScoreAsThread(nr);
9418 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9420 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9422 // if score tape not uploaded, ask for uploading missing tapes later
9423 if (!setup.has_remaining_tapes)
9424 setup.ask_for_remaining_tapes = TRUE;
9426 setup.provide_uploading_tapes = TRUE;
9427 setup.has_remaining_tapes = TRUE;
9429 SaveSetup_ServerSetup();
9432 void SaveServerScore(int nr, boolean tape_saved)
9434 if (!runtime.use_api_server)
9436 PrepareScoreTapesForUpload(leveldir_current->subdir);
9441 ApiAddScoreAsThread(nr, tape_saved, NULL);
9444 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9445 char *score_tape_filename)
9447 if (!runtime.use_api_server)
9450 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9453 void LoadLocalAndServerScore(int nr, boolean download_score)
9455 int last_added_local = scores.last_added_local;
9456 boolean force_last_added = scores.force_last_added;
9458 // needed if only showing server scores
9459 setScoreInfoToDefaults();
9461 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9464 // restore last added local score entry (before merging server scores)
9465 scores.last_added = scores.last_added_local = last_added_local;
9467 if (setup.use_api_server &&
9468 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9470 // load server scores from cache file and trigger update from server
9471 LoadServerScore(nr, download_score);
9473 // merge local scores with scores from server
9477 if (force_last_added)
9478 scores.force_last_added = force_last_added;
9482 // ============================================================================
9483 // setup file functions
9484 // ============================================================================
9486 #define TOKEN_STR_PLAYER_PREFIX "player_"
9489 static struct TokenInfo global_setup_tokens[] =
9493 &setup.player_name, "player_name"
9497 &setup.multiple_users, "multiple_users"
9501 &setup.sound, "sound"
9505 &setup.sound_loops, "repeating_sound_loops"
9509 &setup.sound_music, "background_music"
9513 &setup.sound_simple, "simple_sound_effects"
9517 &setup.toons, "toons"
9521 &setup.global_animations, "global_animations"
9525 &setup.scroll_delay, "scroll_delay"
9529 &setup.forced_scroll_delay, "forced_scroll_delay"
9533 &setup.scroll_delay_value, "scroll_delay_value"
9537 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9541 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9545 &setup.fade_screens, "fade_screens"
9549 &setup.autorecord, "automatic_tape_recording"
9553 &setup.autorecord_after_replay, "autorecord_after_replay"
9557 &setup.auto_pause_on_start, "auto_pause_on_start"
9561 &setup.show_titlescreen, "show_titlescreen"
9565 &setup.quick_doors, "quick_doors"
9569 &setup.team_mode, "team_mode"
9573 &setup.handicap, "handicap"
9577 &setup.skip_levels, "skip_levels"
9581 &setup.increment_levels, "increment_levels"
9585 &setup.auto_play_next_level, "auto_play_next_level"
9589 &setup.count_score_after_game, "count_score_after_game"
9593 &setup.show_scores_after_game, "show_scores_after_game"
9597 &setup.time_limit, "time_limit"
9601 &setup.fullscreen, "fullscreen"
9605 &setup.window_scaling_percent, "window_scaling_percent"
9609 &setup.window_scaling_quality, "window_scaling_quality"
9613 &setup.screen_rendering_mode, "screen_rendering_mode"
9617 &setup.vsync_mode, "vsync_mode"
9621 &setup.ask_on_escape, "ask_on_escape"
9625 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9629 &setup.ask_on_game_over, "ask_on_game_over"
9633 &setup.ask_on_quit_game, "ask_on_quit_game"
9637 &setup.ask_on_quit_program, "ask_on_quit_program"
9641 &setup.quick_switch, "quick_player_switch"
9645 &setup.input_on_focus, "input_on_focus"
9649 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9653 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9657 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9661 &setup.game_speed_extended, "game_speed_extended"
9665 &setup.game_frame_delay, "game_frame_delay"
9669 &setup.sp_show_border_elements, "sp_show_border_elements"
9673 &setup.small_game_graphics, "small_game_graphics"
9677 &setup.show_load_save_buttons, "show_load_save_buttons"
9681 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9685 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9689 &setup.graphics_set, "graphics_set"
9693 &setup.sounds_set, "sounds_set"
9697 &setup.music_set, "music_set"
9701 &setup.override_level_graphics, "override_level_graphics"
9705 &setup.override_level_sounds, "override_level_sounds"
9709 &setup.override_level_music, "override_level_music"
9713 &setup.volume_simple, "volume_simple"
9717 &setup.volume_loops, "volume_loops"
9721 &setup.volume_music, "volume_music"
9725 &setup.network_mode, "network_mode"
9729 &setup.network_player_nr, "network_player"
9733 &setup.network_server_hostname, "network_server_hostname"
9737 &setup.touch.control_type, "touch.control_type"
9741 &setup.touch.move_distance, "touch.move_distance"
9745 &setup.touch.drop_distance, "touch.drop_distance"
9749 &setup.touch.transparency, "touch.transparency"
9753 &setup.touch.draw_outlined, "touch.draw_outlined"
9757 &setup.touch.draw_pressed, "touch.draw_pressed"
9761 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9765 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9769 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9773 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9777 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9781 static struct TokenInfo auto_setup_tokens[] =
9785 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9789 static struct TokenInfo server_setup_tokens[] =
9793 &setup.player_uuid, "player_uuid"
9797 &setup.player_version, "player_version"
9801 &setup.use_api_server, TEST_PREFIX "use_api_server"
9805 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9809 &setup.api_server_password, TEST_PREFIX "api_server_password"
9813 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9817 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9821 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9825 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9829 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9833 static struct TokenInfo editor_setup_tokens[] =
9837 &setup.editor.el_classic, "editor.el_classic"
9841 &setup.editor.el_custom, "editor.el_custom"
9845 &setup.editor.el_user_defined, "editor.el_user_defined"
9849 &setup.editor.el_dynamic, "editor.el_dynamic"
9853 &setup.editor.el_headlines, "editor.el_headlines"
9857 &setup.editor.show_element_token, "editor.show_element_token"
9861 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9865 static struct TokenInfo editor_cascade_setup_tokens[] =
9869 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9873 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
9877 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9881 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9885 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9889 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9893 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9897 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9901 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9905 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9909 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9913 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9917 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9921 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9925 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9929 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9933 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9937 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9941 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9945 static struct TokenInfo shortcut_setup_tokens[] =
9949 &setup.shortcut.save_game, "shortcut.save_game"
9953 &setup.shortcut.load_game, "shortcut.load_game"
9957 &setup.shortcut.restart_game, "shortcut.restart_game"
9961 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9965 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9969 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9973 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9977 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9981 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9985 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9989 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9993 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9997 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10001 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10005 &setup.shortcut.tape_record, "shortcut.tape_record"
10009 &setup.shortcut.tape_play, "shortcut.tape_play"
10013 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10017 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10021 &setup.shortcut.sound_music, "shortcut.sound_music"
10025 &setup.shortcut.snap_left, "shortcut.snap_left"
10029 &setup.shortcut.snap_right, "shortcut.snap_right"
10033 &setup.shortcut.snap_up, "shortcut.snap_up"
10037 &setup.shortcut.snap_down, "shortcut.snap_down"
10041 static struct SetupInputInfo setup_input;
10042 static struct TokenInfo player_setup_tokens[] =
10046 &setup_input.use_joystick, ".use_joystick"
10050 &setup_input.joy.device_name, ".joy.device_name"
10054 &setup_input.joy.xleft, ".joy.xleft"
10058 &setup_input.joy.xmiddle, ".joy.xmiddle"
10062 &setup_input.joy.xright, ".joy.xright"
10066 &setup_input.joy.yupper, ".joy.yupper"
10070 &setup_input.joy.ymiddle, ".joy.ymiddle"
10074 &setup_input.joy.ylower, ".joy.ylower"
10078 &setup_input.joy.snap, ".joy.snap_field"
10082 &setup_input.joy.drop, ".joy.place_bomb"
10086 &setup_input.key.left, ".key.move_left"
10090 &setup_input.key.right, ".key.move_right"
10094 &setup_input.key.up, ".key.move_up"
10098 &setup_input.key.down, ".key.move_down"
10102 &setup_input.key.snap, ".key.snap_field"
10106 &setup_input.key.drop, ".key.place_bomb"
10110 static struct TokenInfo system_setup_tokens[] =
10114 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10118 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10122 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10126 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10130 static struct TokenInfo internal_setup_tokens[] =
10134 &setup.internal.program_title, "program_title"
10138 &setup.internal.program_version, "program_version"
10142 &setup.internal.program_author, "program_author"
10146 &setup.internal.program_email, "program_email"
10150 &setup.internal.program_website, "program_website"
10154 &setup.internal.program_copyright, "program_copyright"
10158 &setup.internal.program_company, "program_company"
10162 &setup.internal.program_icon_file, "program_icon_file"
10166 &setup.internal.default_graphics_set, "default_graphics_set"
10170 &setup.internal.default_sounds_set, "default_sounds_set"
10174 &setup.internal.default_music_set, "default_music_set"
10178 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10182 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10186 &setup.internal.fallback_music_file, "fallback_music_file"
10190 &setup.internal.default_level_series, "default_level_series"
10194 &setup.internal.default_window_width, "default_window_width"
10198 &setup.internal.default_window_height, "default_window_height"
10202 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10206 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10210 &setup.internal.create_user_levelset, "create_user_levelset"
10214 &setup.internal.info_screens_from_main, "info_screens_from_main"
10218 &setup.internal.menu_game, "menu_game"
10222 &setup.internal.menu_engines, "menu_engines"
10226 &setup.internal.menu_editor, "menu_editor"
10230 &setup.internal.menu_graphics, "menu_graphics"
10234 &setup.internal.menu_sound, "menu_sound"
10238 &setup.internal.menu_artwork, "menu_artwork"
10242 &setup.internal.menu_input, "menu_input"
10246 &setup.internal.menu_touch, "menu_touch"
10250 &setup.internal.menu_shortcuts, "menu_shortcuts"
10254 &setup.internal.menu_exit, "menu_exit"
10258 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10262 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10266 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10270 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10274 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10278 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10282 &setup.internal.info_title, "info_title"
10286 &setup.internal.info_elements, "info_elements"
10290 &setup.internal.info_music, "info_music"
10294 &setup.internal.info_credits, "info_credits"
10298 &setup.internal.info_program, "info_program"
10302 &setup.internal.info_version, "info_version"
10306 &setup.internal.info_levelset, "info_levelset"
10310 &setup.internal.info_exit, "info_exit"
10314 static struct TokenInfo debug_setup_tokens[] =
10318 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10322 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10326 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10330 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10334 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10338 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10342 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10346 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10350 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10354 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10358 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10362 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10366 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10370 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10374 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10378 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10382 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10386 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10390 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10394 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10398 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10401 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10405 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10409 &setup.debug.xsn_mode, "debug.xsn_mode"
10413 &setup.debug.xsn_percent, "debug.xsn_percent"
10417 static struct TokenInfo options_setup_tokens[] =
10421 &setup.options.verbose, "options.verbose"
10425 &setup.options.debug, "options.debug"
10429 &setup.options.debug_mode, "options.debug_mode"
10433 static void setSetupInfoToDefaults(struct SetupInfo *si)
10437 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10439 si->multiple_users = TRUE;
10442 si->sound_loops = TRUE;
10443 si->sound_music = TRUE;
10444 si->sound_simple = TRUE;
10446 si->global_animations = TRUE;
10447 si->scroll_delay = TRUE;
10448 si->forced_scroll_delay = FALSE;
10449 si->scroll_delay_value = STD_SCROLL_DELAY;
10450 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10451 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10452 si->fade_screens = TRUE;
10453 si->autorecord = TRUE;
10454 si->autorecord_after_replay = TRUE;
10455 si->auto_pause_on_start = FALSE;
10456 si->show_titlescreen = TRUE;
10457 si->quick_doors = FALSE;
10458 si->team_mode = FALSE;
10459 si->handicap = TRUE;
10460 si->skip_levels = TRUE;
10461 si->increment_levels = TRUE;
10462 si->auto_play_next_level = TRUE;
10463 si->count_score_after_game = TRUE;
10464 si->show_scores_after_game = TRUE;
10465 si->time_limit = TRUE;
10466 si->fullscreen = FALSE;
10467 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10468 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10469 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10470 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10471 si->ask_on_escape = TRUE;
10472 si->ask_on_escape_editor = TRUE;
10473 si->ask_on_game_over = TRUE;
10474 si->ask_on_quit_game = TRUE;
10475 si->ask_on_quit_program = TRUE;
10476 si->quick_switch = FALSE;
10477 si->input_on_focus = FALSE;
10478 si->prefer_aga_graphics = TRUE;
10479 si->prefer_lowpass_sounds = FALSE;
10480 si->prefer_extra_panel_items = TRUE;
10481 si->game_speed_extended = FALSE;
10482 si->game_frame_delay = GAME_FRAME_DELAY;
10483 si->sp_show_border_elements = FALSE;
10484 si->small_game_graphics = FALSE;
10485 si->show_load_save_buttons = FALSE;
10486 si->show_undo_redo_buttons = FALSE;
10487 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10489 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10490 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10491 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10493 si->override_level_graphics = FALSE;
10494 si->override_level_sounds = FALSE;
10495 si->override_level_music = FALSE;
10497 si->volume_simple = 100; // percent
10498 si->volume_loops = 100; // percent
10499 si->volume_music = 100; // percent
10501 si->network_mode = FALSE;
10502 si->network_player_nr = 0; // first player
10503 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10505 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10506 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10507 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10508 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10509 si->touch.draw_outlined = TRUE;
10510 si->touch.draw_pressed = TRUE;
10512 for (i = 0; i < 2; i++)
10514 char *default_grid_button[6][2] =
10520 { "111222", " vv " },
10521 { "111222", " vv " }
10523 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10524 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10525 int min_xsize = MIN(6, grid_xsize);
10526 int min_ysize = MIN(6, grid_ysize);
10527 int startx = grid_xsize - min_xsize;
10528 int starty = grid_ysize - min_ysize;
10531 // virtual buttons grid can only be set to defaults if video is initialized
10532 // (this will be repeated if virtual buttons are not loaded from setup file)
10533 if (video.initialized)
10535 si->touch.grid_xsize[i] = grid_xsize;
10536 si->touch.grid_ysize[i] = grid_ysize;
10540 si->touch.grid_xsize[i] = -1;
10541 si->touch.grid_ysize[i] = -1;
10544 for (x = 0; x < MAX_GRID_XSIZE; x++)
10545 for (y = 0; y < MAX_GRID_YSIZE; y++)
10546 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10548 for (x = 0; x < min_xsize; x++)
10549 for (y = 0; y < min_ysize; y++)
10550 si->touch.grid_button[i][x][starty + y] =
10551 default_grid_button[y][0][x];
10553 for (x = 0; x < min_xsize; x++)
10554 for (y = 0; y < min_ysize; y++)
10555 si->touch.grid_button[i][startx + x][starty + y] =
10556 default_grid_button[y][1][x];
10559 si->touch.grid_initialized = video.initialized;
10561 si->touch.overlay_buttons = FALSE;
10563 si->editor.el_boulderdash = TRUE;
10564 si->editor.el_boulderdash_native = TRUE;
10565 si->editor.el_emerald_mine = TRUE;
10566 si->editor.el_emerald_mine_club = TRUE;
10567 si->editor.el_more = TRUE;
10568 si->editor.el_sokoban = TRUE;
10569 si->editor.el_supaplex = TRUE;
10570 si->editor.el_diamond_caves = TRUE;
10571 si->editor.el_dx_boulderdash = TRUE;
10573 si->editor.el_mirror_magic = TRUE;
10574 si->editor.el_deflektor = TRUE;
10576 si->editor.el_chars = TRUE;
10577 si->editor.el_steel_chars = TRUE;
10579 si->editor.el_classic = TRUE;
10580 si->editor.el_custom = TRUE;
10582 si->editor.el_user_defined = FALSE;
10583 si->editor.el_dynamic = TRUE;
10585 si->editor.el_headlines = TRUE;
10587 si->editor.show_element_token = FALSE;
10589 si->editor.show_read_only_warning = TRUE;
10591 si->editor.use_template_for_new_levels = TRUE;
10593 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10594 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10595 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10596 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10597 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10599 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10600 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10601 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10602 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10603 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10605 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10606 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10607 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10608 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10609 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10610 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10612 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10613 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10614 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10616 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10617 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10618 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10619 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10621 for (i = 0; i < MAX_PLAYERS; i++)
10623 si->input[i].use_joystick = FALSE;
10624 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10625 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10626 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10627 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10628 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10629 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10630 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10631 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10632 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10633 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10634 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10635 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10636 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10637 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10638 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10641 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10642 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10643 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10644 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10646 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10647 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10648 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10649 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10650 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10651 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10652 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10654 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10656 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10657 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10658 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10660 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10661 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10662 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10664 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10665 si->internal.choose_from_top_leveldir = FALSE;
10666 si->internal.show_scaling_in_title = TRUE;
10667 si->internal.create_user_levelset = TRUE;
10668 si->internal.info_screens_from_main = FALSE;
10670 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10671 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10673 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10674 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10675 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10676 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10677 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10678 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10679 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10680 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10681 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10682 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10684 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10685 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10686 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10687 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10688 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10689 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10690 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10691 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10692 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10693 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10695 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10696 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10698 si->debug.show_frames_per_second = FALSE;
10700 si->debug.xsn_mode = AUTO;
10701 si->debug.xsn_percent = 0;
10703 si->options.verbose = FALSE;
10704 si->options.debug = FALSE;
10705 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10707 #if defined(PLATFORM_ANDROID)
10708 si->fullscreen = TRUE;
10709 si->touch.overlay_buttons = TRUE;
10712 setHideSetupEntry(&setup.debug.xsn_mode);
10715 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10717 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10720 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10722 si->player_uuid = NULL; // (will be set later)
10723 si->player_version = 1; // (will be set later)
10725 si->use_api_server = TRUE;
10726 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10727 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10728 si->ask_for_uploading_tapes = TRUE;
10729 si->ask_for_remaining_tapes = FALSE;
10730 si->provide_uploading_tapes = TRUE;
10731 si->ask_for_using_api_server = TRUE;
10732 si->has_remaining_tapes = FALSE;
10735 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10737 si->editor_cascade.el_bd = TRUE;
10738 si->editor_cascade.el_bd_native = TRUE;
10739 si->editor_cascade.el_em = TRUE;
10740 si->editor_cascade.el_emc = TRUE;
10741 si->editor_cascade.el_rnd = TRUE;
10742 si->editor_cascade.el_sb = TRUE;
10743 si->editor_cascade.el_sp = TRUE;
10744 si->editor_cascade.el_dc = TRUE;
10745 si->editor_cascade.el_dx = TRUE;
10747 si->editor_cascade.el_mm = TRUE;
10748 si->editor_cascade.el_df = TRUE;
10750 si->editor_cascade.el_chars = FALSE;
10751 si->editor_cascade.el_steel_chars = FALSE;
10752 si->editor_cascade.el_ce = FALSE;
10753 si->editor_cascade.el_ge = FALSE;
10754 si->editor_cascade.el_es = FALSE;
10755 si->editor_cascade.el_ref = FALSE;
10756 si->editor_cascade.el_user = FALSE;
10757 si->editor_cascade.el_dynamic = FALSE;
10760 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10762 static char *getHideSetupToken(void *setup_value)
10764 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10766 if (setup_value != NULL)
10767 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10769 return hide_setup_token;
10772 void setHideSetupEntry(void *setup_value)
10774 char *hide_setup_token = getHideSetupToken(setup_value);
10776 if (hide_setup_hash == NULL)
10777 hide_setup_hash = newSetupFileHash();
10779 if (setup_value != NULL)
10780 setHashEntry(hide_setup_hash, hide_setup_token, "");
10783 void removeHideSetupEntry(void *setup_value)
10785 char *hide_setup_token = getHideSetupToken(setup_value);
10787 if (setup_value != NULL)
10788 removeHashEntry(hide_setup_hash, hide_setup_token);
10791 boolean hideSetupEntry(void *setup_value)
10793 char *hide_setup_token = getHideSetupToken(setup_value);
10795 return (setup_value != NULL &&
10796 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10799 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10800 struct TokenInfo *token_info,
10801 int token_nr, char *token_text)
10803 char *token_hide_text = getStringCat2(token_text, ".hide");
10804 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10806 // set the value of this setup option in the setup option structure
10807 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10809 // check if this setup option should be hidden in the setup menu
10810 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10811 setHideSetupEntry(token_info[token_nr].value);
10813 free(token_hide_text);
10816 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10817 struct TokenInfo *token_info,
10820 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10821 token_info[token_nr].text);
10824 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10828 if (!setup_file_hash)
10831 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10832 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10834 setup.touch.grid_initialized = TRUE;
10835 for (i = 0; i < 2; i++)
10837 int grid_xsize = setup.touch.grid_xsize[i];
10838 int grid_ysize = setup.touch.grid_ysize[i];
10841 // if virtual buttons are not loaded from setup file, repeat initializing
10842 // virtual buttons grid with default values later when video is initialized
10843 if (grid_xsize == -1 ||
10846 setup.touch.grid_initialized = FALSE;
10851 for (y = 0; y < grid_ysize; y++)
10853 char token_string[MAX_LINE_LEN];
10855 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10857 char *value_string = getHashEntry(setup_file_hash, token_string);
10859 if (value_string == NULL)
10862 for (x = 0; x < grid_xsize; x++)
10864 char c = value_string[x];
10866 setup.touch.grid_button[i][x][y] =
10867 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10872 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10873 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10875 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10876 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10878 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10882 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10884 setup_input = setup.input[pnr];
10885 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10887 char full_token[100];
10889 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10890 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10893 setup.input[pnr] = setup_input;
10896 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10897 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10899 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10900 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10902 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10903 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10905 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10906 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10908 setHideRelatedSetupEntries();
10911 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10915 if (!setup_file_hash)
10918 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10919 setSetupInfo(auto_setup_tokens, i,
10920 getHashEntry(setup_file_hash,
10921 auto_setup_tokens[i].text));
10924 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10928 if (!setup_file_hash)
10931 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10932 setSetupInfo(server_setup_tokens, i,
10933 getHashEntry(setup_file_hash,
10934 server_setup_tokens[i].text));
10937 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10941 if (!setup_file_hash)
10944 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10945 setSetupInfo(editor_cascade_setup_tokens, i,
10946 getHashEntry(setup_file_hash,
10947 editor_cascade_setup_tokens[i].text));
10950 void LoadUserNames(void)
10952 int last_user_nr = user.nr;
10955 if (global.user_names != NULL)
10957 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10958 checked_free(global.user_names[i]);
10960 checked_free(global.user_names);
10963 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10965 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10969 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10971 if (setup_file_hash)
10973 char *player_name = getHashEntry(setup_file_hash, "player_name");
10975 global.user_names[i] = getFixedUserName(player_name);
10977 freeSetupFileHash(setup_file_hash);
10980 if (global.user_names[i] == NULL)
10981 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10984 user.nr = last_user_nr;
10987 void LoadSetupFromFilename(char *filename)
10989 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10991 if (setup_file_hash)
10993 decodeSetupFileHash_Default(setup_file_hash);
10995 freeSetupFileHash(setup_file_hash);
10999 Debug("setup", "using default setup values");
11003 static void LoadSetup_SpecialPostProcessing(void)
11005 char *player_name_new;
11007 // needed to work around problems with fixed length strings
11008 player_name_new = getFixedUserName(setup.player_name);
11009 free(setup.player_name);
11010 setup.player_name = player_name_new;
11012 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11013 if (setup.scroll_delay == FALSE)
11015 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11016 setup.scroll_delay = TRUE; // now always "on"
11019 // make sure that scroll delay value stays inside valid range
11020 setup.scroll_delay_value =
11021 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11024 void LoadSetup_Default(void)
11028 // always start with reliable default values
11029 setSetupInfoToDefaults(&setup);
11031 // try to load setup values from default setup file
11032 filename = getDefaultSetupFilename();
11034 if (fileExists(filename))
11035 LoadSetupFromFilename(filename);
11037 // try to load setup values from platform setup file
11038 filename = getPlatformSetupFilename();
11040 if (fileExists(filename))
11041 LoadSetupFromFilename(filename);
11043 // try to load setup values from user setup file
11044 filename = getSetupFilename();
11046 LoadSetupFromFilename(filename);
11048 LoadSetup_SpecialPostProcessing();
11051 void LoadSetup_AutoSetup(void)
11053 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11054 SetupFileHash *setup_file_hash = NULL;
11056 // always start with reliable default values
11057 setSetupInfoToDefaults_AutoSetup(&setup);
11059 setup_file_hash = loadSetupFileHash(filename);
11061 if (setup_file_hash)
11063 decodeSetupFileHash_AutoSetup(setup_file_hash);
11065 freeSetupFileHash(setup_file_hash);
11071 void LoadSetup_ServerSetup(void)
11073 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11074 SetupFileHash *setup_file_hash = NULL;
11076 // always start with reliable default values
11077 setSetupInfoToDefaults_ServerSetup(&setup);
11079 setup_file_hash = loadSetupFileHash(filename);
11081 if (setup_file_hash)
11083 decodeSetupFileHash_ServerSetup(setup_file_hash);
11085 freeSetupFileHash(setup_file_hash);
11090 if (setup.player_uuid == NULL)
11092 // player UUID does not yet exist in setup file
11093 setup.player_uuid = getStringCopy(getUUID());
11094 setup.player_version = 2;
11096 SaveSetup_ServerSetup();
11100 void LoadSetup_EditorCascade(void)
11102 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11103 SetupFileHash *setup_file_hash = NULL;
11105 // always start with reliable default values
11106 setSetupInfoToDefaults_EditorCascade(&setup);
11108 setup_file_hash = loadSetupFileHash(filename);
11110 if (setup_file_hash)
11112 decodeSetupFileHash_EditorCascade(setup_file_hash);
11114 freeSetupFileHash(setup_file_hash);
11120 void LoadSetup(void)
11122 LoadSetup_Default();
11123 LoadSetup_AutoSetup();
11124 LoadSetup_ServerSetup();
11125 LoadSetup_EditorCascade();
11128 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11129 char *mapping_line)
11131 char mapping_guid[MAX_LINE_LEN];
11132 char *mapping_start, *mapping_end;
11134 // get GUID from game controller mapping line: copy complete line
11135 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11136 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11138 // get GUID from game controller mapping line: cut after GUID part
11139 mapping_start = strchr(mapping_guid, ',');
11140 if (mapping_start != NULL)
11141 *mapping_start = '\0';
11143 // cut newline from game controller mapping line
11144 mapping_end = strchr(mapping_line, '\n');
11145 if (mapping_end != NULL)
11146 *mapping_end = '\0';
11148 // add mapping entry to game controller mappings hash
11149 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11152 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11157 if (!(file = fopen(filename, MODE_READ)))
11159 Warn("cannot read game controller mappings file '%s'", filename);
11164 while (!feof(file))
11166 char line[MAX_LINE_LEN];
11168 if (!fgets(line, MAX_LINE_LEN, file))
11171 addGameControllerMappingToHash(mappings_hash, line);
11177 void SaveSetup_Default(void)
11179 char *filename = getSetupFilename();
11183 InitUserDataDirectory();
11185 if (!(file = fopen(filename, MODE_WRITE)))
11187 Warn("cannot write setup file '%s'", filename);
11192 fprintFileHeader(file, SETUP_FILENAME);
11194 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11196 // just to make things nicer :)
11197 if (global_setup_tokens[i].value == &setup.multiple_users ||
11198 global_setup_tokens[i].value == &setup.sound ||
11199 global_setup_tokens[i].value == &setup.graphics_set ||
11200 global_setup_tokens[i].value == &setup.volume_simple ||
11201 global_setup_tokens[i].value == &setup.network_mode ||
11202 global_setup_tokens[i].value == &setup.touch.control_type ||
11203 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11204 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11205 fprintf(file, "\n");
11207 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11210 for (i = 0; i < 2; i++)
11212 int grid_xsize = setup.touch.grid_xsize[i];
11213 int grid_ysize = setup.touch.grid_ysize[i];
11216 fprintf(file, "\n");
11218 for (y = 0; y < grid_ysize; y++)
11220 char token_string[MAX_LINE_LEN];
11221 char value_string[MAX_LINE_LEN];
11223 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11225 for (x = 0; x < grid_xsize; x++)
11227 char c = setup.touch.grid_button[i][x][y];
11229 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11232 value_string[grid_xsize] = '\0';
11234 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11238 fprintf(file, "\n");
11239 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11240 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11242 fprintf(file, "\n");
11243 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11244 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11246 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11250 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11251 fprintf(file, "\n");
11253 setup_input = setup.input[pnr];
11254 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11255 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11258 fprintf(file, "\n");
11259 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11260 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11262 // (internal setup values not saved to user setup file)
11264 fprintf(file, "\n");
11265 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11266 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11267 setup.debug.xsn_mode != AUTO)
11268 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11270 fprintf(file, "\n");
11271 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11272 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11276 SetFilePermissions(filename, PERMS_PRIVATE);
11279 void SaveSetup_AutoSetup(void)
11281 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11285 InitUserDataDirectory();
11287 if (!(file = fopen(filename, MODE_WRITE)))
11289 Warn("cannot write auto setup file '%s'", filename);
11296 fprintFileHeader(file, AUTOSETUP_FILENAME);
11298 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11299 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11303 SetFilePermissions(filename, PERMS_PRIVATE);
11308 void SaveSetup_ServerSetup(void)
11310 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11314 InitUserDataDirectory();
11316 if (!(file = fopen(filename, MODE_WRITE)))
11318 Warn("cannot write server setup file '%s'", filename);
11325 fprintFileHeader(file, SERVERSETUP_FILENAME);
11327 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11329 // just to make things nicer :)
11330 if (server_setup_tokens[i].value == &setup.use_api_server)
11331 fprintf(file, "\n");
11333 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11338 SetFilePermissions(filename, PERMS_PRIVATE);
11343 void SaveSetup_EditorCascade(void)
11345 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11349 InitUserDataDirectory();
11351 if (!(file = fopen(filename, MODE_WRITE)))
11353 Warn("cannot write editor cascade state file '%s'", filename);
11360 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11362 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11363 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11367 SetFilePermissions(filename, PERMS_PRIVATE);
11372 void SaveSetup(void)
11374 SaveSetup_Default();
11375 SaveSetup_AutoSetup();
11376 SaveSetup_ServerSetup();
11377 SaveSetup_EditorCascade();
11380 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11385 if (!(file = fopen(filename, MODE_WRITE)))
11387 Warn("cannot write game controller mappings file '%s'", filename);
11392 BEGIN_HASH_ITERATION(mappings_hash, itr)
11394 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11396 END_HASH_ITERATION(mappings_hash, itr)
11401 void SaveSetup_AddGameControllerMapping(char *mapping)
11403 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11404 SetupFileHash *mappings_hash = newSetupFileHash();
11406 InitUserDataDirectory();
11408 // load existing personal game controller mappings
11409 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11411 // add new mapping to personal game controller mappings
11412 addGameControllerMappingToHash(mappings_hash, mapping);
11414 // save updated personal game controller mappings
11415 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11417 freeSetupFileHash(mappings_hash);
11421 void LoadCustomElementDescriptions(void)
11423 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11424 SetupFileHash *setup_file_hash;
11427 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11429 if (element_info[i].custom_description != NULL)
11431 free(element_info[i].custom_description);
11432 element_info[i].custom_description = NULL;
11436 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11439 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11441 char *token = getStringCat2(element_info[i].token_name, ".name");
11442 char *value = getHashEntry(setup_file_hash, token);
11445 element_info[i].custom_description = getStringCopy(value);
11450 freeSetupFileHash(setup_file_hash);
11453 static int getElementFromToken(char *token)
11455 char *value = getHashEntry(element_token_hash, token);
11458 return atoi(value);
11460 Warn("unknown element token '%s'", token);
11462 return EL_UNDEFINED;
11465 void FreeGlobalAnimEventInfo(void)
11467 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11469 if (gaei->event_list == NULL)
11474 for (i = 0; i < gaei->num_event_lists; i++)
11476 checked_free(gaei->event_list[i]->event_value);
11477 checked_free(gaei->event_list[i]);
11480 checked_free(gaei->event_list);
11482 gaei->event_list = NULL;
11483 gaei->num_event_lists = 0;
11486 static int AddGlobalAnimEventList(void)
11488 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11489 int list_pos = gaei->num_event_lists++;
11491 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11492 sizeof(struct GlobalAnimEventListInfo *));
11494 gaei->event_list[list_pos] =
11495 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11497 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11499 gaeli->event_value = NULL;
11500 gaeli->num_event_values = 0;
11505 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11507 // do not add empty global animation events
11508 if (event_value == ANIM_EVENT_NONE)
11511 // if list position is undefined, create new list
11512 if (list_pos == ANIM_EVENT_UNDEFINED)
11513 list_pos = AddGlobalAnimEventList();
11515 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11516 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11517 int value_pos = gaeli->num_event_values++;
11519 gaeli->event_value = checked_realloc(gaeli->event_value,
11520 gaeli->num_event_values * sizeof(int *));
11522 gaeli->event_value[value_pos] = event_value;
11527 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11529 if (list_pos == ANIM_EVENT_UNDEFINED)
11530 return ANIM_EVENT_NONE;
11532 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11533 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11535 return gaeli->event_value[value_pos];
11538 int GetGlobalAnimEventValueCount(int list_pos)
11540 if (list_pos == ANIM_EVENT_UNDEFINED)
11543 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11544 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11546 return gaeli->num_event_values;
11549 // This function checks if a string <s> of the format "string1, string2, ..."
11550 // exactly contains a string <s_contained>.
11552 static boolean string_has_parameter(char *s, char *s_contained)
11556 if (s == NULL || s_contained == NULL)
11559 if (strlen(s_contained) > strlen(s))
11562 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11564 char next_char = s[strlen(s_contained)];
11566 // check if next character is delimiter or whitespace
11567 if (next_char == ',' || next_char == '\0' ||
11568 next_char == ' ' || next_char == '\t')
11572 // check if string contains another parameter string after a comma
11573 substring = strchr(s, ',');
11574 if (substring == NULL) // string does not contain a comma
11577 // advance string pointer to next character after the comma
11580 // skip potential whitespaces after the comma
11581 while (*substring == ' ' || *substring == '\t')
11584 return string_has_parameter(substring, s_contained);
11587 static int get_anim_parameter_value_ce(char *s)
11590 char *pattern_1 = "ce_change:custom_";
11591 char *pattern_2 = ".page_";
11592 int pattern_1_len = strlen(pattern_1);
11593 char *matching_char = strstr(s_ptr, pattern_1);
11594 int result = ANIM_EVENT_NONE;
11596 if (matching_char == NULL)
11597 return ANIM_EVENT_NONE;
11599 result = ANIM_EVENT_CE_CHANGE;
11601 s_ptr = matching_char + pattern_1_len;
11603 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11604 if (*s_ptr >= '0' && *s_ptr <= '9')
11606 int gic_ce_nr = (*s_ptr++ - '0');
11608 if (*s_ptr >= '0' && *s_ptr <= '9')
11610 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11612 if (*s_ptr >= '0' && *s_ptr <= '9')
11613 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11616 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11617 return ANIM_EVENT_NONE;
11619 // custom element stored as 0 to 255
11622 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11626 // invalid custom element number specified
11628 return ANIM_EVENT_NONE;
11631 // check for change page number ("page_X" or "page_XX") (optional)
11632 if (strPrefix(s_ptr, pattern_2))
11634 s_ptr += strlen(pattern_2);
11636 if (*s_ptr >= '0' && *s_ptr <= '9')
11638 int gic_page_nr = (*s_ptr++ - '0');
11640 if (*s_ptr >= '0' && *s_ptr <= '9')
11641 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11643 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11644 return ANIM_EVENT_NONE;
11646 // change page stored as 1 to 32 (0 means "all change pages")
11648 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11652 // invalid animation part number specified
11654 return ANIM_EVENT_NONE;
11658 // discard result if next character is neither delimiter nor whitespace
11659 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11660 *s_ptr == ' ' || *s_ptr == '\t'))
11661 return ANIM_EVENT_NONE;
11666 static int get_anim_parameter_value(char *s)
11668 int event_value[] =
11676 char *pattern_1[] =
11684 char *pattern_2 = ".part_";
11685 char *matching_char = NULL;
11687 int pattern_1_len = 0;
11688 int result = ANIM_EVENT_NONE;
11691 result = get_anim_parameter_value_ce(s);
11693 if (result != ANIM_EVENT_NONE)
11696 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11698 matching_char = strstr(s_ptr, pattern_1[i]);
11699 pattern_1_len = strlen(pattern_1[i]);
11700 result = event_value[i];
11702 if (matching_char != NULL)
11706 if (matching_char == NULL)
11707 return ANIM_EVENT_NONE;
11709 s_ptr = matching_char + pattern_1_len;
11711 // check for main animation number ("anim_X" or "anim_XX")
11712 if (*s_ptr >= '0' && *s_ptr <= '9')
11714 int gic_anim_nr = (*s_ptr++ - '0');
11716 if (*s_ptr >= '0' && *s_ptr <= '9')
11717 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11719 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11720 return ANIM_EVENT_NONE;
11722 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11726 // invalid main animation number specified
11728 return ANIM_EVENT_NONE;
11731 // check for animation part number ("part_X" or "part_XX") (optional)
11732 if (strPrefix(s_ptr, pattern_2))
11734 s_ptr += strlen(pattern_2);
11736 if (*s_ptr >= '0' && *s_ptr <= '9')
11738 int gic_part_nr = (*s_ptr++ - '0');
11740 if (*s_ptr >= '0' && *s_ptr <= '9')
11741 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11743 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11744 return ANIM_EVENT_NONE;
11746 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11750 // invalid animation part number specified
11752 return ANIM_EVENT_NONE;
11756 // discard result if next character is neither delimiter nor whitespace
11757 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11758 *s_ptr == ' ' || *s_ptr == '\t'))
11759 return ANIM_EVENT_NONE;
11764 static int get_anim_parameter_values(char *s)
11766 int list_pos = ANIM_EVENT_UNDEFINED;
11767 int event_value = ANIM_EVENT_DEFAULT;
11769 if (string_has_parameter(s, "any"))
11770 event_value |= ANIM_EVENT_ANY;
11772 if (string_has_parameter(s, "click:self") ||
11773 string_has_parameter(s, "click") ||
11774 string_has_parameter(s, "self"))
11775 event_value |= ANIM_EVENT_SELF;
11777 if (string_has_parameter(s, "unclick:any"))
11778 event_value |= ANIM_EVENT_UNCLICK_ANY;
11780 // if animation event found, add it to global animation event list
11781 if (event_value != ANIM_EVENT_NONE)
11782 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11786 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11787 event_value = get_anim_parameter_value(s);
11789 // if animation event found, add it to global animation event list
11790 if (event_value != ANIM_EVENT_NONE)
11791 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11793 // continue with next part of the string, starting with next comma
11794 s = strchr(s + 1, ',');
11800 static int get_anim_action_parameter_value(char *token)
11802 // check most common default case first to massively speed things up
11803 if (strEqual(token, ARG_UNDEFINED))
11804 return ANIM_EVENT_ACTION_NONE;
11806 int result = getImageIDFromToken(token);
11810 char *gfx_token = getStringCat2("gfx.", token);
11812 result = getImageIDFromToken(gfx_token);
11814 checked_free(gfx_token);
11819 Key key = getKeyFromX11KeyName(token);
11821 if (key != KSYM_UNDEFINED)
11822 result = -(int)key;
11829 result = get_hash_from_key(token); // unsigned int => int
11830 result = ABS(result); // may be negative now
11831 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11833 setHashEntry(anim_url_hash, int2str(result, 0), token);
11838 result = ANIM_EVENT_ACTION_NONE;
11843 int get_parameter_value(char *value_raw, char *suffix, int type)
11845 char *value = getStringToLower(value_raw);
11846 int result = 0; // probably a save default value
11848 if (strEqual(suffix, ".direction"))
11850 result = (strEqual(value, "left") ? MV_LEFT :
11851 strEqual(value, "right") ? MV_RIGHT :
11852 strEqual(value, "up") ? MV_UP :
11853 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11855 else if (strEqual(suffix, ".position"))
11857 result = (strEqual(value, "left") ? POS_LEFT :
11858 strEqual(value, "right") ? POS_RIGHT :
11859 strEqual(value, "top") ? POS_TOP :
11860 strEqual(value, "upper") ? POS_UPPER :
11861 strEqual(value, "middle") ? POS_MIDDLE :
11862 strEqual(value, "lower") ? POS_LOWER :
11863 strEqual(value, "bottom") ? POS_BOTTOM :
11864 strEqual(value, "any") ? POS_ANY :
11865 strEqual(value, "ce") ? POS_CE :
11866 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
11867 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11869 else if (strEqual(suffix, ".align"))
11871 result = (strEqual(value, "left") ? ALIGN_LEFT :
11872 strEqual(value, "right") ? ALIGN_RIGHT :
11873 strEqual(value, "center") ? ALIGN_CENTER :
11874 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11876 else if (strEqual(suffix, ".valign"))
11878 result = (strEqual(value, "top") ? VALIGN_TOP :
11879 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11880 strEqual(value, "middle") ? VALIGN_MIDDLE :
11881 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11883 else if (strEqual(suffix, ".anim_mode"))
11885 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11886 string_has_parameter(value, "loop") ? ANIM_LOOP :
11887 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11888 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11889 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11890 string_has_parameter(value, "random") ? ANIM_RANDOM :
11891 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11892 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11893 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11894 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11895 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11896 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11897 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11898 string_has_parameter(value, "all") ? ANIM_ALL :
11899 string_has_parameter(value, "tiled") ? ANIM_TILED :
11900 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
11903 if (string_has_parameter(value, "once"))
11904 result |= ANIM_ONCE;
11906 if (string_has_parameter(value, "reverse"))
11907 result |= ANIM_REVERSE;
11909 if (string_has_parameter(value, "opaque_player"))
11910 result |= ANIM_OPAQUE_PLAYER;
11912 if (string_has_parameter(value, "static_panel"))
11913 result |= ANIM_STATIC_PANEL;
11915 else if (strEqual(suffix, ".init_event") ||
11916 strEqual(suffix, ".anim_event"))
11918 result = get_anim_parameter_values(value);
11920 else if (strEqual(suffix, ".init_delay_action") ||
11921 strEqual(suffix, ".anim_delay_action") ||
11922 strEqual(suffix, ".post_delay_action") ||
11923 strEqual(suffix, ".init_event_action") ||
11924 strEqual(suffix, ".anim_event_action"))
11926 result = get_anim_action_parameter_value(value_raw);
11928 else if (strEqual(suffix, ".class"))
11930 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11931 get_hash_from_key(value));
11933 else if (strEqual(suffix, ".style"))
11935 result = STYLE_DEFAULT;
11937 if (string_has_parameter(value, "accurate_borders"))
11938 result |= STYLE_ACCURATE_BORDERS;
11940 if (string_has_parameter(value, "inner_corners"))
11941 result |= STYLE_INNER_CORNERS;
11943 if (string_has_parameter(value, "reverse"))
11944 result |= STYLE_REVERSE;
11946 if (string_has_parameter(value, "leftmost_position"))
11947 result |= STYLE_LEFTMOST_POSITION;
11949 if (string_has_parameter(value, "block_clicks"))
11950 result |= STYLE_BLOCK;
11952 if (string_has_parameter(value, "passthrough_clicks"))
11953 result |= STYLE_PASSTHROUGH;
11955 if (string_has_parameter(value, "multiple_actions"))
11956 result |= STYLE_MULTIPLE_ACTIONS;
11958 if (string_has_parameter(value, "consume_ce_event"))
11959 result |= STYLE_CONSUME_CE_EVENT;
11961 else if (strEqual(suffix, ".fade_mode"))
11963 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11964 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11965 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
11966 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
11967 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11968 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11969 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11970 FADE_MODE_DEFAULT);
11972 else if (strEqual(suffix, ".auto_delay_unit"))
11974 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11975 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11976 AUTO_DELAY_UNIT_DEFAULT);
11978 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11980 result = gfx.get_font_from_token_function(value);
11982 else // generic parameter of type integer or boolean
11984 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11985 type == TYPE_INTEGER ? get_integer_from_string(value) :
11986 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11987 ARG_UNDEFINED_VALUE);
11995 static int get_token_parameter_value(char *token, char *value_raw)
11999 if (token == NULL || value_raw == NULL)
12000 return ARG_UNDEFINED_VALUE;
12002 suffix = strrchr(token, '.');
12003 if (suffix == NULL)
12006 if (strEqual(suffix, ".element"))
12007 return getElementFromToken(value_raw);
12009 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12010 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12013 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12014 boolean ignore_defaults)
12018 for (i = 0; image_config_vars[i].token != NULL; i++)
12020 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12022 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12023 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12027 *image_config_vars[i].value =
12028 get_token_parameter_value(image_config_vars[i].token, value);
12032 void InitMenuDesignSettings_Static(void)
12034 // always start with reliable default values from static default config
12035 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12038 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12042 // the following initializes hierarchical values from static configuration
12044 // special case: initialize "ARG_DEFAULT" values in static default config
12045 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12046 titlescreen_initial_first_default.fade_mode =
12047 title_initial_first_default.fade_mode;
12048 titlescreen_initial_first_default.fade_delay =
12049 title_initial_first_default.fade_delay;
12050 titlescreen_initial_first_default.post_delay =
12051 title_initial_first_default.post_delay;
12052 titlescreen_initial_first_default.auto_delay =
12053 title_initial_first_default.auto_delay;
12054 titlescreen_initial_first_default.auto_delay_unit =
12055 title_initial_first_default.auto_delay_unit;
12056 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12057 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12058 titlescreen_first_default.post_delay = title_first_default.post_delay;
12059 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12060 titlescreen_first_default.auto_delay_unit =
12061 title_first_default.auto_delay_unit;
12062 titlemessage_initial_first_default.fade_mode =
12063 title_initial_first_default.fade_mode;
12064 titlemessage_initial_first_default.fade_delay =
12065 title_initial_first_default.fade_delay;
12066 titlemessage_initial_first_default.post_delay =
12067 title_initial_first_default.post_delay;
12068 titlemessage_initial_first_default.auto_delay =
12069 title_initial_first_default.auto_delay;
12070 titlemessage_initial_first_default.auto_delay_unit =
12071 title_initial_first_default.auto_delay_unit;
12072 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12073 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12074 titlemessage_first_default.post_delay = title_first_default.post_delay;
12075 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12076 titlemessage_first_default.auto_delay_unit =
12077 title_first_default.auto_delay_unit;
12079 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12080 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12081 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12082 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12083 titlescreen_initial_default.auto_delay_unit =
12084 title_initial_default.auto_delay_unit;
12085 titlescreen_default.fade_mode = title_default.fade_mode;
12086 titlescreen_default.fade_delay = title_default.fade_delay;
12087 titlescreen_default.post_delay = title_default.post_delay;
12088 titlescreen_default.auto_delay = title_default.auto_delay;
12089 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12090 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12091 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12092 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12093 titlemessage_initial_default.auto_delay_unit =
12094 title_initial_default.auto_delay_unit;
12095 titlemessage_default.fade_mode = title_default.fade_mode;
12096 titlemessage_default.fade_delay = title_default.fade_delay;
12097 titlemessage_default.post_delay = title_default.post_delay;
12098 titlemessage_default.auto_delay = title_default.auto_delay;
12099 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12101 // special case: initialize "ARG_DEFAULT" values in static default config
12102 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12103 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12105 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12106 titlescreen_first[i] = titlescreen_first_default;
12107 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12108 titlemessage_first[i] = titlemessage_first_default;
12110 titlescreen_initial[i] = titlescreen_initial_default;
12111 titlescreen[i] = titlescreen_default;
12112 titlemessage_initial[i] = titlemessage_initial_default;
12113 titlemessage[i] = titlemessage_default;
12116 // special case: initialize "ARG_DEFAULT" values in static default config
12117 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12118 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12120 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12123 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12124 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12125 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12128 // special case: initialize "ARG_DEFAULT" values in static default config
12129 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12130 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12132 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12133 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12134 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12136 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12139 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12143 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12147 struct XY *dst, *src;
12149 game_buttons_xy[] =
12151 { &game.button.save, &game.button.stop },
12152 { &game.button.pause2, &game.button.pause },
12153 { &game.button.load, &game.button.play },
12154 { &game.button.undo, &game.button.stop },
12155 { &game.button.redo, &game.button.play },
12161 // special case: initialize later added SETUP list size from LEVELS value
12162 if (menu.list_size[GAME_MODE_SETUP] == -1)
12163 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12165 // set default position for snapshot buttons to stop/pause/play buttons
12166 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12167 if ((*game_buttons_xy[i].dst).x == -1 &&
12168 (*game_buttons_xy[i].dst).y == -1)
12169 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12171 // --------------------------------------------------------------------------
12172 // dynamic viewports (including playfield margins, borders and alignments)
12173 // --------------------------------------------------------------------------
12175 // dynamic viewports currently only supported for landscape mode
12176 int display_width = MAX(video.display_width, video.display_height);
12177 int display_height = MIN(video.display_width, video.display_height);
12179 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12181 struct RectWithBorder *vp_window = &viewport.window[i];
12182 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12183 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12184 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12185 boolean dynamic_window_width = (vp_window->min_width != -1);
12186 boolean dynamic_window_height = (vp_window->min_height != -1);
12187 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12188 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12190 // adjust window size if min/max width/height is specified
12192 if (vp_window->min_width != -1)
12194 int window_width = display_width;
12196 // when using static window height, use aspect ratio of display
12197 if (vp_window->min_height == -1)
12198 window_width = vp_window->height * display_width / display_height;
12200 vp_window->width = MAX(vp_window->min_width, window_width);
12203 if (vp_window->min_height != -1)
12205 int window_height = display_height;
12207 // when using static window width, use aspect ratio of display
12208 if (vp_window->min_width == -1)
12209 window_height = vp_window->width * display_height / display_width;
12211 vp_window->height = MAX(vp_window->min_height, window_height);
12214 if (vp_window->max_width != -1)
12215 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12217 if (vp_window->max_height != -1)
12218 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12220 int playfield_width = vp_window->width;
12221 int playfield_height = vp_window->height;
12223 // adjust playfield size and position according to specified margins
12225 playfield_width -= vp_playfield->margin_left;
12226 playfield_width -= vp_playfield->margin_right;
12228 playfield_height -= vp_playfield->margin_top;
12229 playfield_height -= vp_playfield->margin_bottom;
12231 // adjust playfield size if min/max width/height is specified
12233 if (vp_playfield->min_width != -1)
12234 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12236 if (vp_playfield->min_height != -1)
12237 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12239 if (vp_playfield->max_width != -1)
12240 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12242 if (vp_playfield->max_height != -1)
12243 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12245 // adjust playfield position according to specified alignment
12247 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12248 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12249 else if (vp_playfield->align == ALIGN_CENTER)
12250 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12251 else if (vp_playfield->align == ALIGN_RIGHT)
12252 vp_playfield->x += playfield_width - vp_playfield->width;
12254 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12255 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12256 else if (vp_playfield->valign == VALIGN_MIDDLE)
12257 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12258 else if (vp_playfield->valign == VALIGN_BOTTOM)
12259 vp_playfield->y += playfield_height - vp_playfield->height;
12261 vp_playfield->x += vp_playfield->margin_left;
12262 vp_playfield->y += vp_playfield->margin_top;
12264 // adjust individual playfield borders if only default border is specified
12266 if (vp_playfield->border_left == -1)
12267 vp_playfield->border_left = vp_playfield->border_size;
12268 if (vp_playfield->border_right == -1)
12269 vp_playfield->border_right = vp_playfield->border_size;
12270 if (vp_playfield->border_top == -1)
12271 vp_playfield->border_top = vp_playfield->border_size;
12272 if (vp_playfield->border_bottom == -1)
12273 vp_playfield->border_bottom = vp_playfield->border_size;
12275 // set dynamic playfield borders if borders are specified as undefined
12276 // (but only if window size was dynamic and playfield size was static)
12278 if (dynamic_window_width && !dynamic_playfield_width)
12280 if (vp_playfield->border_left == -1)
12282 vp_playfield->border_left = (vp_playfield->x -
12283 vp_playfield->margin_left);
12284 vp_playfield->x -= vp_playfield->border_left;
12285 vp_playfield->width += vp_playfield->border_left;
12288 if (vp_playfield->border_right == -1)
12290 vp_playfield->border_right = (vp_window->width -
12292 vp_playfield->width -
12293 vp_playfield->margin_right);
12294 vp_playfield->width += vp_playfield->border_right;
12298 if (dynamic_window_height && !dynamic_playfield_height)
12300 if (vp_playfield->border_top == -1)
12302 vp_playfield->border_top = (vp_playfield->y -
12303 vp_playfield->margin_top);
12304 vp_playfield->y -= vp_playfield->border_top;
12305 vp_playfield->height += vp_playfield->border_top;
12308 if (vp_playfield->border_bottom == -1)
12310 vp_playfield->border_bottom = (vp_window->height -
12312 vp_playfield->height -
12313 vp_playfield->margin_bottom);
12314 vp_playfield->height += vp_playfield->border_bottom;
12318 // adjust playfield size to be a multiple of a defined alignment tile size
12320 int align_size = vp_playfield->align_size;
12321 int playfield_xtiles = vp_playfield->width / align_size;
12322 int playfield_ytiles = vp_playfield->height / align_size;
12323 int playfield_width_corrected = playfield_xtiles * align_size;
12324 int playfield_height_corrected = playfield_ytiles * align_size;
12325 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12326 i == GFX_SPECIAL_ARG_EDITOR);
12328 if (is_playfield_mode &&
12329 dynamic_playfield_width &&
12330 vp_playfield->width != playfield_width_corrected)
12332 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12334 vp_playfield->width = playfield_width_corrected;
12336 if (vp_playfield->align == ALIGN_LEFT)
12338 vp_playfield->border_left += playfield_xdiff;
12340 else if (vp_playfield->align == ALIGN_RIGHT)
12342 vp_playfield->border_right += playfield_xdiff;
12344 else if (vp_playfield->align == ALIGN_CENTER)
12346 int border_left_diff = playfield_xdiff / 2;
12347 int border_right_diff = playfield_xdiff - border_left_diff;
12349 vp_playfield->border_left += border_left_diff;
12350 vp_playfield->border_right += border_right_diff;
12354 if (is_playfield_mode &&
12355 dynamic_playfield_height &&
12356 vp_playfield->height != playfield_height_corrected)
12358 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12360 vp_playfield->height = playfield_height_corrected;
12362 if (vp_playfield->valign == VALIGN_TOP)
12364 vp_playfield->border_top += playfield_ydiff;
12366 else if (vp_playfield->align == VALIGN_BOTTOM)
12368 vp_playfield->border_right += playfield_ydiff;
12370 else if (vp_playfield->align == VALIGN_MIDDLE)
12372 int border_top_diff = playfield_ydiff / 2;
12373 int border_bottom_diff = playfield_ydiff - border_top_diff;
12375 vp_playfield->border_top += border_top_diff;
12376 vp_playfield->border_bottom += border_bottom_diff;
12380 // adjust door positions according to specified alignment
12382 for (j = 0; j < 2; j++)
12384 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12386 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12387 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12388 else if (vp_door->align == ALIGN_CENTER)
12389 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12390 else if (vp_door->align == ALIGN_RIGHT)
12391 vp_door->x += vp_window->width - vp_door->width;
12393 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12394 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12395 else if (vp_door->valign == VALIGN_MIDDLE)
12396 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12397 else if (vp_door->valign == VALIGN_BOTTOM)
12398 vp_door->y += vp_window->height - vp_door->height;
12403 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12407 struct XYTileSize *dst, *src;
12410 editor_buttons_xy[] =
12413 &editor.button.element_left, &editor.palette.element_left,
12414 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12417 &editor.button.element_middle, &editor.palette.element_middle,
12418 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12421 &editor.button.element_right, &editor.palette.element_right,
12422 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12429 // set default position for element buttons to element graphics
12430 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12432 if ((*editor_buttons_xy[i].dst).x == -1 &&
12433 (*editor_buttons_xy[i].dst).y == -1)
12435 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12437 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12439 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12443 // adjust editor palette rows and columns if specified to be dynamic
12445 if (editor.palette.cols == -1)
12447 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12448 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12449 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12451 editor.palette.cols = (vp_width - sc_width) / bt_width;
12453 if (editor.palette.x == -1)
12455 int palette_width = editor.palette.cols * bt_width + sc_width;
12457 editor.palette.x = (vp_width - palette_width) / 2;
12461 if (editor.palette.rows == -1)
12463 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12464 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12465 int tx_height = getFontHeight(FONT_TEXT_2);
12467 editor.palette.rows = (vp_height - tx_height) / bt_height;
12469 if (editor.palette.y == -1)
12471 int palette_height = editor.palette.rows * bt_height + tx_height;
12473 editor.palette.y = (vp_height - palette_height) / 2;
12478 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12479 boolean initialize)
12481 // special case: check if network and preview player positions are redefined,
12482 // to compare this later against the main menu level preview being redefined
12483 struct TokenIntPtrInfo menu_config_players[] =
12485 { "main.network_players.x", &menu.main.network_players.redefined },
12486 { "main.network_players.y", &menu.main.network_players.redefined },
12487 { "main.preview_players.x", &menu.main.preview_players.redefined },
12488 { "main.preview_players.y", &menu.main.preview_players.redefined },
12489 { "preview.x", &preview.redefined },
12490 { "preview.y", &preview.redefined }
12496 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12497 *menu_config_players[i].value = FALSE;
12501 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12502 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12503 *menu_config_players[i].value = TRUE;
12507 static void InitMenuDesignSettings_PreviewPlayers(void)
12509 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12512 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12514 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12517 static void LoadMenuDesignSettingsFromFilename(char *filename)
12519 static struct TitleFadingInfo tfi;
12520 static struct TitleMessageInfo tmi;
12521 static struct TokenInfo title_tokens[] =
12523 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12524 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12525 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12526 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12527 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12531 static struct TokenInfo titlemessage_tokens[] =
12533 { TYPE_INTEGER, &tmi.x, ".x" },
12534 { TYPE_INTEGER, &tmi.y, ".y" },
12535 { TYPE_INTEGER, &tmi.width, ".width" },
12536 { TYPE_INTEGER, &tmi.height, ".height" },
12537 { TYPE_INTEGER, &tmi.chars, ".chars" },
12538 { TYPE_INTEGER, &tmi.lines, ".lines" },
12539 { TYPE_INTEGER, &tmi.align, ".align" },
12540 { TYPE_INTEGER, &tmi.valign, ".valign" },
12541 { TYPE_INTEGER, &tmi.font, ".font" },
12542 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12543 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12544 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12545 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12546 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12547 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12548 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12549 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12550 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12556 struct TitleFadingInfo *info;
12561 // initialize first titles from "enter screen" definitions, if defined
12562 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12563 { &title_first_default, "menu.enter_screen.TITLE" },
12565 // initialize title screens from "next screen" definitions, if defined
12566 { &title_initial_default, "menu.next_screen.TITLE" },
12567 { &title_default, "menu.next_screen.TITLE" },
12573 struct TitleMessageInfo *array;
12576 titlemessage_arrays[] =
12578 // initialize first titles from "enter screen" definitions, if defined
12579 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12580 { titlescreen_first, "menu.enter_screen.TITLE" },
12581 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12582 { titlemessage_first, "menu.enter_screen.TITLE" },
12584 // initialize titles from "next screen" definitions, if defined
12585 { titlescreen_initial, "menu.next_screen.TITLE" },
12586 { titlescreen, "menu.next_screen.TITLE" },
12587 { titlemessage_initial, "menu.next_screen.TITLE" },
12588 { titlemessage, "menu.next_screen.TITLE" },
12590 // overwrite titles with title definitions, if defined
12591 { titlescreen_initial_first, "[title_initial]" },
12592 { titlescreen_first, "[title]" },
12593 { titlemessage_initial_first, "[title_initial]" },
12594 { titlemessage_first, "[title]" },
12596 { titlescreen_initial, "[title_initial]" },
12597 { titlescreen, "[title]" },
12598 { titlemessage_initial, "[title_initial]" },
12599 { titlemessage, "[title]" },
12601 // overwrite titles with title screen/message definitions, if defined
12602 { titlescreen_initial_first, "[titlescreen_initial]" },
12603 { titlescreen_first, "[titlescreen]" },
12604 { titlemessage_initial_first, "[titlemessage_initial]" },
12605 { titlemessage_first, "[titlemessage]" },
12607 { titlescreen_initial, "[titlescreen_initial]" },
12608 { titlescreen, "[titlescreen]" },
12609 { titlemessage_initial, "[titlemessage_initial]" },
12610 { titlemessage, "[titlemessage]" },
12614 SetupFileHash *setup_file_hash;
12617 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12620 // the following initializes hierarchical values from dynamic configuration
12622 // special case: initialize with default values that may be overwritten
12623 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12624 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12626 struct TokenIntPtrInfo menu_config[] =
12628 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12629 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12630 { "menu.list_size", &menu.list_size[i] }
12633 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12635 char *token = menu_config[j].token;
12636 char *value = getHashEntry(setup_file_hash, token);
12639 *menu_config[j].value = get_integer_from_string(value);
12643 // special case: initialize with default values that may be overwritten
12644 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12645 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12647 struct TokenIntPtrInfo menu_config[] =
12649 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12650 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12651 { "menu.list_size.INFO", &menu.list_size_info[i] },
12652 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12653 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12656 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12658 char *token = menu_config[j].token;
12659 char *value = getHashEntry(setup_file_hash, token);
12662 *menu_config[j].value = get_integer_from_string(value);
12666 // special case: initialize with default values that may be overwritten
12667 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12668 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12670 struct TokenIntPtrInfo menu_config[] =
12672 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12673 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12676 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12678 char *token = menu_config[j].token;
12679 char *value = getHashEntry(setup_file_hash, token);
12682 *menu_config[j].value = get_integer_from_string(value);
12686 // special case: initialize with default values that may be overwritten
12687 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12688 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12690 struct TokenIntPtrInfo menu_config[] =
12692 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12693 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12694 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12695 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12696 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12697 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12698 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12699 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12700 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12701 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12704 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12706 char *token = menu_config[j].token;
12707 char *value = getHashEntry(setup_file_hash, token);
12710 *menu_config[j].value = get_integer_from_string(value);
12714 // special case: initialize with default values that may be overwritten
12715 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12716 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12718 struct TokenIntPtrInfo menu_config[] =
12720 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12721 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12722 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12723 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12724 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12725 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12726 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12727 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12728 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12731 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12733 char *token = menu_config[j].token;
12734 char *value = getHashEntry(setup_file_hash, token);
12737 *menu_config[j].value = get_token_parameter_value(token, value);
12741 // special case: initialize with default values that may be overwritten
12742 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12743 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12747 char *token_prefix;
12748 struct RectWithBorder *struct_ptr;
12752 { "viewport.window", &viewport.window[i] },
12753 { "viewport.playfield", &viewport.playfield[i] },
12754 { "viewport.door_1", &viewport.door_1[i] },
12755 { "viewport.door_2", &viewport.door_2[i] }
12758 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12760 struct TokenIntPtrInfo vp_config[] =
12762 { ".x", &vp_struct[j].struct_ptr->x },
12763 { ".y", &vp_struct[j].struct_ptr->y },
12764 { ".width", &vp_struct[j].struct_ptr->width },
12765 { ".height", &vp_struct[j].struct_ptr->height },
12766 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12767 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12768 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12769 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12770 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12771 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12772 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12773 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12774 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12775 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12776 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12777 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12778 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12779 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12780 { ".align", &vp_struct[j].struct_ptr->align },
12781 { ".valign", &vp_struct[j].struct_ptr->valign }
12784 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12786 char *token = getStringCat2(vp_struct[j].token_prefix,
12787 vp_config[k].token);
12788 char *value = getHashEntry(setup_file_hash, token);
12791 *vp_config[k].value = get_token_parameter_value(token, value);
12798 // special case: initialize with default values that may be overwritten
12799 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12800 for (i = 0; title_info[i].info != NULL; i++)
12802 struct TitleFadingInfo *info = title_info[i].info;
12803 char *base_token = title_info[i].text;
12805 for (j = 0; title_tokens[j].type != -1; j++)
12807 char *token = getStringCat2(base_token, title_tokens[j].text);
12808 char *value = getHashEntry(setup_file_hash, token);
12812 int parameter_value = get_token_parameter_value(token, value);
12816 *(int *)title_tokens[j].value = (int)parameter_value;
12825 // special case: initialize with default values that may be overwritten
12826 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12827 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12829 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12830 char *base_token = titlemessage_arrays[i].text;
12832 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12834 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12835 char *value = getHashEntry(setup_file_hash, token);
12839 int parameter_value = get_token_parameter_value(token, value);
12841 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12845 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12846 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12848 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12858 // read (and overwrite with) values that may be specified in config file
12859 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12861 // special case: check if network and preview player positions are redefined
12862 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
12864 freeSetupFileHash(setup_file_hash);
12867 void LoadMenuDesignSettings(void)
12869 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12871 InitMenuDesignSettings_Static();
12872 InitMenuDesignSettings_SpecialPreProcessing();
12873 InitMenuDesignSettings_PreviewPlayers();
12875 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12877 // first look for special settings configured in level series config
12878 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12880 if (fileExists(filename_base))
12881 LoadMenuDesignSettingsFromFilename(filename_base);
12884 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12886 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12887 LoadMenuDesignSettingsFromFilename(filename_local);
12889 InitMenuDesignSettings_SpecialPostProcessing();
12892 void LoadMenuDesignSettings_AfterGraphics(void)
12894 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12897 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
12898 boolean ignore_defaults)
12902 for (i = 0; sound_config_vars[i].token != NULL; i++)
12904 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
12906 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12907 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12911 *sound_config_vars[i].value =
12912 get_token_parameter_value(sound_config_vars[i].token, value);
12916 void InitSoundSettings_Static(void)
12918 // always start with reliable default values from static default config
12919 InitSoundSettings_FromHash(sound_config_hash, FALSE);
12922 static void LoadSoundSettingsFromFilename(char *filename)
12924 SetupFileHash *setup_file_hash;
12926 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12929 // read (and overwrite with) values that may be specified in config file
12930 InitSoundSettings_FromHash(setup_file_hash, TRUE);
12932 freeSetupFileHash(setup_file_hash);
12935 void LoadSoundSettings(void)
12937 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12939 InitSoundSettings_Static();
12941 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
12943 // first look for special settings configured in level series config
12944 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
12946 if (fileExists(filename_base))
12947 LoadSoundSettingsFromFilename(filename_base);
12950 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
12952 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12953 LoadSoundSettingsFromFilename(filename_local);
12956 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12958 char *filename = getEditorSetupFilename();
12959 SetupFileList *setup_file_list, *list;
12960 SetupFileHash *element_hash;
12961 int num_unknown_tokens = 0;
12964 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12967 element_hash = newSetupFileHash();
12969 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12970 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12972 // determined size may be larger than needed (due to unknown elements)
12974 for (list = setup_file_list; list != NULL; list = list->next)
12977 // add space for up to 3 more elements for padding that may be needed
12978 *num_elements += 3;
12980 // free memory for old list of elements, if needed
12981 checked_free(*elements);
12983 // allocate memory for new list of elements
12984 *elements = checked_malloc(*num_elements * sizeof(int));
12987 for (list = setup_file_list; list != NULL; list = list->next)
12989 char *value = getHashEntry(element_hash, list->token);
12991 if (value == NULL) // try to find obsolete token mapping
12993 char *mapped_token = get_mapped_token(list->token);
12995 if (mapped_token != NULL)
12997 value = getHashEntry(element_hash, mapped_token);
12999 free(mapped_token);
13005 (*elements)[(*num_elements)++] = atoi(value);
13009 if (num_unknown_tokens == 0)
13012 Warn("unknown token(s) found in config file:");
13013 Warn("- config file: '%s'", filename);
13015 num_unknown_tokens++;
13018 Warn("- token: '%s'", list->token);
13022 if (num_unknown_tokens > 0)
13025 while (*num_elements % 4) // pad with empty elements, if needed
13026 (*elements)[(*num_elements)++] = EL_EMPTY;
13028 freeSetupFileList(setup_file_list);
13029 freeSetupFileHash(element_hash);
13032 for (i = 0; i < *num_elements; i++)
13033 Debug("editor", "element '%s' [%d]\n",
13034 element_info[(*elements)[i]].token_name, (*elements)[i]);
13038 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13041 SetupFileHash *setup_file_hash = NULL;
13042 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13043 char *filename_music, *filename_prefix, *filename_info;
13049 token_to_value_ptr[] =
13051 { "title_header", &tmp_music_file_info.title_header },
13052 { "artist_header", &tmp_music_file_info.artist_header },
13053 { "album_header", &tmp_music_file_info.album_header },
13054 { "year_header", &tmp_music_file_info.year_header },
13055 { "played_header", &tmp_music_file_info.played_header },
13057 { "title", &tmp_music_file_info.title },
13058 { "artist", &tmp_music_file_info.artist },
13059 { "album", &tmp_music_file_info.album },
13060 { "year", &tmp_music_file_info.year },
13061 { "played", &tmp_music_file_info.played },
13067 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13068 getCustomMusicFilename(basename));
13070 if (filename_music == NULL)
13073 // ---------- try to replace file extension ----------
13075 filename_prefix = getStringCopy(filename_music);
13076 if (strrchr(filename_prefix, '.') != NULL)
13077 *strrchr(filename_prefix, '.') = '\0';
13078 filename_info = getStringCat2(filename_prefix, ".txt");
13080 if (fileExists(filename_info))
13081 setup_file_hash = loadSetupFileHash(filename_info);
13083 free(filename_prefix);
13084 free(filename_info);
13086 if (setup_file_hash == NULL)
13088 // ---------- try to add file extension ----------
13090 filename_prefix = getStringCopy(filename_music);
13091 filename_info = getStringCat2(filename_prefix, ".txt");
13093 if (fileExists(filename_info))
13094 setup_file_hash = loadSetupFileHash(filename_info);
13096 free(filename_prefix);
13097 free(filename_info);
13100 if (setup_file_hash == NULL)
13103 // ---------- music file info found ----------
13105 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13107 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13109 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13111 *token_to_value_ptr[i].value_ptr =
13112 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13115 tmp_music_file_info.basename = getStringCopy(basename);
13116 tmp_music_file_info.music = music;
13117 tmp_music_file_info.is_sound = is_sound;
13119 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13120 *new_music_file_info = tmp_music_file_info;
13122 return new_music_file_info;
13125 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13127 return get_music_file_info_ext(basename, music, FALSE);
13130 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13132 return get_music_file_info_ext(basename, sound, TRUE);
13135 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13136 char *basename, boolean is_sound)
13138 for (; list != NULL; list = list->next)
13139 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13145 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13147 return music_info_listed_ext(list, basename, FALSE);
13150 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13152 return music_info_listed_ext(list, basename, TRUE);
13155 void LoadMusicInfo(void)
13157 int num_music_noconf = getMusicListSize_NoConf();
13158 int num_music = getMusicListSize();
13159 int num_sounds = getSoundListSize();
13160 struct FileInfo *music, *sound;
13161 struct MusicFileInfo *next, **new;
13165 while (music_file_info != NULL)
13167 next = music_file_info->next;
13169 checked_free(music_file_info->basename);
13171 checked_free(music_file_info->title_header);
13172 checked_free(music_file_info->artist_header);
13173 checked_free(music_file_info->album_header);
13174 checked_free(music_file_info->year_header);
13175 checked_free(music_file_info->played_header);
13177 checked_free(music_file_info->title);
13178 checked_free(music_file_info->artist);
13179 checked_free(music_file_info->album);
13180 checked_free(music_file_info->year);
13181 checked_free(music_file_info->played);
13183 free(music_file_info);
13185 music_file_info = next;
13188 new = &music_file_info;
13190 // get (configured or unconfigured) music file info for all levels
13191 for (i = leveldir_current->first_level;
13192 i <= leveldir_current->last_level; i++)
13196 if (levelset.music[i] != MUS_UNDEFINED)
13198 // get music file info for configured level music
13199 music_nr = levelset.music[i];
13201 else if (num_music_noconf > 0)
13203 // get music file info for unconfigured level music
13204 int level_pos = i - leveldir_current->first_level;
13206 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13213 char *basename = getMusicInfoEntryFilename(music_nr);
13215 if (basename == NULL)
13218 if (!music_info_listed(music_file_info, basename))
13220 *new = get_music_file_info(basename, music_nr);
13223 new = &(*new)->next;
13227 // get music file info for all remaining configured music files
13228 for (i = 0; i < num_music; i++)
13230 music = getMusicListEntry(i);
13232 if (music->filename == NULL)
13235 if (strEqual(music->filename, UNDEFINED_FILENAME))
13238 // a configured file may be not recognized as music
13239 if (!FileIsMusic(music->filename))
13242 if (!music_info_listed(music_file_info, music->filename))
13244 *new = get_music_file_info(music->filename, i);
13247 new = &(*new)->next;
13251 // get sound file info for all configured sound files
13252 for (i = 0; i < num_sounds; i++)
13254 sound = getSoundListEntry(i);
13256 if (sound->filename == NULL)
13259 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13262 // a configured file may be not recognized as sound
13263 if (!FileIsSound(sound->filename))
13266 if (!sound_info_listed(music_file_info, sound->filename))
13268 *new = get_sound_file_info(sound->filename, i);
13270 new = &(*new)->next;
13274 // add pointers to previous list nodes
13276 struct MusicFileInfo *node = music_file_info;
13278 while (node != NULL)
13281 node->next->prev = node;
13287 static void add_helpanim_entry(int element, int action, int direction,
13288 int delay, int *num_list_entries)
13290 struct HelpAnimInfo *new_list_entry;
13291 (*num_list_entries)++;
13294 checked_realloc(helpanim_info,
13295 *num_list_entries * sizeof(struct HelpAnimInfo));
13296 new_list_entry = &helpanim_info[*num_list_entries - 1];
13298 new_list_entry->element = element;
13299 new_list_entry->action = action;
13300 new_list_entry->direction = direction;
13301 new_list_entry->delay = delay;
13304 static void print_unknown_token(char *filename, char *token, int token_nr)
13309 Warn("unknown token(s) found in config file:");
13310 Warn("- config file: '%s'", filename);
13313 Warn("- token: '%s'", token);
13316 static void print_unknown_token_end(int token_nr)
13322 void LoadHelpAnimInfo(void)
13324 char *filename = getHelpAnimFilename();
13325 SetupFileList *setup_file_list = NULL, *list;
13326 SetupFileHash *element_hash, *action_hash, *direction_hash;
13327 int num_list_entries = 0;
13328 int num_unknown_tokens = 0;
13331 if (fileExists(filename))
13332 setup_file_list = loadSetupFileList(filename);
13334 if (setup_file_list == NULL)
13336 // use reliable default values from static configuration
13337 SetupFileList *insert_ptr;
13339 insert_ptr = setup_file_list =
13340 newSetupFileList(helpanim_config[0].token,
13341 helpanim_config[0].value);
13343 for (i = 1; helpanim_config[i].token; i++)
13344 insert_ptr = addListEntry(insert_ptr,
13345 helpanim_config[i].token,
13346 helpanim_config[i].value);
13349 element_hash = newSetupFileHash();
13350 action_hash = newSetupFileHash();
13351 direction_hash = newSetupFileHash();
13353 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13354 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13356 for (i = 0; i < NUM_ACTIONS; i++)
13357 setHashEntry(action_hash, element_action_info[i].suffix,
13358 i_to_a(element_action_info[i].value));
13360 // do not store direction index (bit) here, but direction value!
13361 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13362 setHashEntry(direction_hash, element_direction_info[i].suffix,
13363 i_to_a(1 << element_direction_info[i].value));
13365 for (list = setup_file_list; list != NULL; list = list->next)
13367 char *element_token, *action_token, *direction_token;
13368 char *element_value, *action_value, *direction_value;
13369 int delay = atoi(list->value);
13371 if (strEqual(list->token, "end"))
13373 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13378 /* first try to break element into element/action/direction parts;
13379 if this does not work, also accept combined "element[.act][.dir]"
13380 elements (like "dynamite.active"), which are unique elements */
13382 if (strchr(list->token, '.') == NULL) // token contains no '.'
13384 element_value = getHashEntry(element_hash, list->token);
13385 if (element_value != NULL) // element found
13386 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13387 &num_list_entries);
13390 // no further suffixes found -- this is not an element
13391 print_unknown_token(filename, list->token, num_unknown_tokens++);
13397 // token has format "<prefix>.<something>"
13399 action_token = strchr(list->token, '.'); // suffix may be action ...
13400 direction_token = action_token; // ... or direction
13402 element_token = getStringCopy(list->token);
13403 *strchr(element_token, '.') = '\0';
13405 element_value = getHashEntry(element_hash, element_token);
13407 if (element_value == NULL) // this is no element
13409 element_value = getHashEntry(element_hash, list->token);
13410 if (element_value != NULL) // combined element found
13411 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13412 &num_list_entries);
13414 print_unknown_token(filename, list->token, num_unknown_tokens++);
13416 free(element_token);
13421 action_value = getHashEntry(action_hash, action_token);
13423 if (action_value != NULL) // action found
13425 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13426 &num_list_entries);
13428 free(element_token);
13433 direction_value = getHashEntry(direction_hash, direction_token);
13435 if (direction_value != NULL) // direction found
13437 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13438 &num_list_entries);
13440 free(element_token);
13445 if (strchr(action_token + 1, '.') == NULL)
13447 // no further suffixes found -- this is not an action nor direction
13449 element_value = getHashEntry(element_hash, list->token);
13450 if (element_value != NULL) // combined element found
13451 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13452 &num_list_entries);
13454 print_unknown_token(filename, list->token, num_unknown_tokens++);
13456 free(element_token);
13461 // token has format "<prefix>.<suffix>.<something>"
13463 direction_token = strchr(action_token + 1, '.');
13465 action_token = getStringCopy(action_token);
13466 *strchr(action_token + 1, '.') = '\0';
13468 action_value = getHashEntry(action_hash, action_token);
13470 if (action_value == NULL) // this is no action
13472 element_value = getHashEntry(element_hash, list->token);
13473 if (element_value != NULL) // combined element found
13474 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13475 &num_list_entries);
13477 print_unknown_token(filename, list->token, num_unknown_tokens++);
13479 free(element_token);
13480 free(action_token);
13485 direction_value = getHashEntry(direction_hash, direction_token);
13487 if (direction_value != NULL) // direction found
13489 add_helpanim_entry(atoi(element_value), atoi(action_value),
13490 atoi(direction_value), delay, &num_list_entries);
13492 free(element_token);
13493 free(action_token);
13498 // this is no direction
13500 element_value = getHashEntry(element_hash, list->token);
13501 if (element_value != NULL) // combined element found
13502 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13503 &num_list_entries);
13505 print_unknown_token(filename, list->token, num_unknown_tokens++);
13507 free(element_token);
13508 free(action_token);
13511 print_unknown_token_end(num_unknown_tokens);
13513 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13514 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13516 freeSetupFileList(setup_file_list);
13517 freeSetupFileHash(element_hash);
13518 freeSetupFileHash(action_hash);
13519 freeSetupFileHash(direction_hash);
13522 for (i = 0; i < num_list_entries; i++)
13523 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13524 EL_NAME(helpanim_info[i].element),
13525 helpanim_info[i].element,
13526 helpanim_info[i].action,
13527 helpanim_info[i].direction,
13528 helpanim_info[i].delay);
13532 void LoadHelpTextInfo(void)
13534 char *filename = getHelpTextFilename();
13537 if (helptext_info != NULL)
13539 freeSetupFileHash(helptext_info);
13540 helptext_info = NULL;
13543 if (fileExists(filename))
13544 helptext_info = loadSetupFileHash(filename);
13546 if (helptext_info == NULL)
13548 // use reliable default values from static configuration
13549 helptext_info = newSetupFileHash();
13551 for (i = 0; helptext_config[i].token; i++)
13552 setHashEntry(helptext_info,
13553 helptext_config[i].token,
13554 helptext_config[i].value);
13558 BEGIN_HASH_ITERATION(helptext_info, itr)
13560 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13561 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13563 END_HASH_ITERATION(hash, itr)
13568 // ----------------------------------------------------------------------------
13570 // ----------------------------------------------------------------------------
13572 #define MAX_NUM_CONVERT_LEVELS 1000
13574 void ConvertLevels(void)
13576 static LevelDirTree *convert_leveldir = NULL;
13577 static int convert_level_nr = -1;
13578 static int num_levels_handled = 0;
13579 static int num_levels_converted = 0;
13580 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13583 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13584 global.convert_leveldir);
13586 if (convert_leveldir == NULL)
13587 Fail("no such level identifier: '%s'", global.convert_leveldir);
13589 leveldir_current = convert_leveldir;
13591 if (global.convert_level_nr != -1)
13593 convert_leveldir->first_level = global.convert_level_nr;
13594 convert_leveldir->last_level = global.convert_level_nr;
13597 convert_level_nr = convert_leveldir->first_level;
13599 PrintLine("=", 79);
13600 Print("Converting levels\n");
13601 PrintLine("-", 79);
13602 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13603 Print("Level series name: '%s'\n", convert_leveldir->name);
13604 Print("Level series author: '%s'\n", convert_leveldir->author);
13605 Print("Number of levels: %d\n", convert_leveldir->levels);
13606 PrintLine("=", 79);
13609 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13610 levels_failed[i] = FALSE;
13612 while (convert_level_nr <= convert_leveldir->last_level)
13614 char *level_filename;
13617 level_nr = convert_level_nr++;
13619 Print("Level %03d: ", level_nr);
13621 LoadLevel(level_nr);
13622 if (level.no_level_file || level.no_valid_file)
13624 Print("(no level)\n");
13628 Print("converting level ... ");
13631 // special case: conversion of some EMC levels as requested by ACME
13632 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13635 level_filename = getDefaultLevelFilename(level_nr);
13636 new_level = !fileExists(level_filename);
13640 SaveLevel(level_nr);
13642 num_levels_converted++;
13644 Print("converted.\n");
13648 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13649 levels_failed[level_nr] = TRUE;
13651 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13654 num_levels_handled++;
13658 PrintLine("=", 79);
13659 Print("Number of levels handled: %d\n", num_levels_handled);
13660 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13661 (num_levels_handled ?
13662 num_levels_converted * 100 / num_levels_handled : 0));
13663 PrintLine("-", 79);
13664 Print("Summary (for automatic parsing by scripts):\n");
13665 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13666 convert_leveldir->identifier, num_levels_converted,
13667 num_levels_handled,
13668 (num_levels_handled ?
13669 num_levels_converted * 100 / num_levels_handled : 0));
13671 if (num_levels_handled != num_levels_converted)
13673 Print(", FAILED:");
13674 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13675 if (levels_failed[i])
13680 PrintLine("=", 79);
13682 CloseAllAndExit(0);
13686 // ----------------------------------------------------------------------------
13687 // create and save images for use in level sketches (raw BMP format)
13688 // ----------------------------------------------------------------------------
13690 void CreateLevelSketchImages(void)
13696 InitElementPropertiesGfxElement();
13698 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13699 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13701 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13703 int element = getMappedElement(i);
13704 char basename1[16];
13705 char basename2[16];
13709 sprintf(basename1, "%04d.bmp", i);
13710 sprintf(basename2, "%04ds.bmp", i);
13712 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13713 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13715 DrawSizedElement(0, 0, element, TILESIZE);
13716 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13718 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13719 Fail("cannot save level sketch image file '%s'", filename1);
13721 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13722 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13724 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13725 Fail("cannot save level sketch image file '%s'", filename2);
13730 // create corresponding SQL statements (for normal and small images)
13733 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13734 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13737 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13738 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13740 // optional: create content for forum level sketch demonstration post
13742 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13745 FreeBitmap(bitmap1);
13746 FreeBitmap(bitmap2);
13749 fprintf(stderr, "\n");
13751 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13753 CloseAllAndExit(0);
13757 // ----------------------------------------------------------------------------
13758 // create and save images for element collecting animations (raw BMP format)
13759 // ----------------------------------------------------------------------------
13761 static boolean createCollectImage(int element)
13763 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13766 void CreateCollectElementImages(void)
13770 int anim_frames = num_steps - 1;
13771 int tile_size = TILESIZE;
13772 int anim_width = tile_size * anim_frames;
13773 int anim_height = tile_size;
13774 int num_collect_images = 0;
13775 int pos_collect_images = 0;
13777 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13778 if (createCollectImage(i))
13779 num_collect_images++;
13781 Info("Creating %d element collecting animation images ...",
13782 num_collect_images);
13784 int dst_width = anim_width * 2;
13785 int dst_height = anim_height * num_collect_images / 2;
13786 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13787 char *basename_bmp = "RocksCollect.bmp";
13788 char *basename_png = "RocksCollect.png";
13789 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13790 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13791 int len_filename_bmp = strlen(filename_bmp);
13792 int len_filename_png = strlen(filename_png);
13793 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13794 char cmd_convert[max_command_len];
13796 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13800 // force using RGBA surface for destination bitmap
13801 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13802 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13804 dst_bitmap->surface =
13805 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13807 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13809 if (!createCollectImage(i))
13812 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13813 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13814 int graphic = el2img(i);
13815 char *token_name = element_info[i].token_name;
13816 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13817 Bitmap *src_bitmap;
13820 Info("- creating collecting image for '%s' ...", token_name);
13822 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13824 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13825 tile_size, tile_size, 0, 0);
13827 // force using RGBA surface for temporary bitmap (using transparent black)
13828 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13829 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13831 tmp_bitmap->surface =
13832 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13834 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13836 for (j = 0; j < anim_frames; j++)
13838 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13839 int frame_size = frame_size_final * num_steps;
13840 int offset = (tile_size - frame_size_final) / 2;
13841 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13843 while (frame_size > frame_size_final)
13847 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13849 FreeBitmap(frame_bitmap);
13851 frame_bitmap = half_bitmap;
13854 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13855 frame_size_final, frame_size_final,
13856 dst_x + j * tile_size + offset, dst_y + offset);
13858 FreeBitmap(frame_bitmap);
13861 tmp_bitmap->surface_masked = NULL;
13863 FreeBitmap(tmp_bitmap);
13865 pos_collect_images++;
13868 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13869 Fail("cannot save element collecting image file '%s'", filename_bmp);
13871 FreeBitmap(dst_bitmap);
13873 Info("Converting image file from BMP to PNG ...");
13875 if (system(cmd_convert) != 0)
13876 Fail("converting image file failed");
13878 unlink(filename_bmp);
13882 CloseAllAndExit(0);
13886 // ----------------------------------------------------------------------------
13887 // create and save images for custom and group elements (raw BMP format)
13888 // ----------------------------------------------------------------------------
13890 void CreateCustomElementImages(char *directory)
13892 char *src_basename = "RocksCE-template.ilbm";
13893 char *dst_basename = "RocksCE.bmp";
13894 char *src_filename = getPath2(directory, src_basename);
13895 char *dst_filename = getPath2(directory, dst_basename);
13896 Bitmap *src_bitmap;
13898 int yoffset_ce = 0;
13899 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13902 InitVideoDefaults();
13904 ReCreateBitmap(&backbuffer, video.width, video.height);
13906 src_bitmap = LoadImage(src_filename);
13908 bitmap = CreateBitmap(TILEX * 16 * 2,
13909 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13912 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13919 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13920 TILEX * x, TILEY * y + yoffset_ce);
13922 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13924 TILEX * x + TILEX * 16,
13925 TILEY * y + yoffset_ce);
13927 for (j = 2; j >= 0; j--)
13931 BlitBitmap(src_bitmap, bitmap,
13932 TILEX + c * 7, 0, 6, 10,
13933 TILEX * x + 6 + j * 7,
13934 TILEY * y + 11 + yoffset_ce);
13936 BlitBitmap(src_bitmap, bitmap,
13937 TILEX + c * 8, TILEY, 6, 10,
13938 TILEX * 16 + TILEX * x + 6 + j * 8,
13939 TILEY * y + 10 + yoffset_ce);
13945 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13952 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13953 TILEX * x, TILEY * y + yoffset_ge);
13955 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13957 TILEX * x + TILEX * 16,
13958 TILEY * y + yoffset_ge);
13960 for (j = 1; j >= 0; j--)
13964 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13965 TILEX * x + 6 + j * 10,
13966 TILEY * y + 11 + yoffset_ge);
13968 BlitBitmap(src_bitmap, bitmap,
13969 TILEX + c * 8, TILEY + 12, 6, 10,
13970 TILEX * 16 + TILEX * x + 10 + j * 8,
13971 TILEY * y + 10 + yoffset_ge);
13977 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13978 Fail("cannot save CE graphics file '%s'", dst_filename);
13980 FreeBitmap(bitmap);
13982 CloseAllAndExit(0);