1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
187 -1, SAVE_CONF_ALWAYS,
188 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
194 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
201 &li.use_step_counter, FALSE
206 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
207 &li.wind_direction_initial, MV_NONE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
213 &li.em_slippery_gems, FALSE
218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
219 &li.use_custom_template, FALSE
224 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
225 &li.can_move_into_acid_bits, ~0 // default: everything can
230 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
231 &li.dont_collide_with_bits, ~0 // default: always deadly
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
237 &li.em_explodes_by_fire, FALSE
242 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
243 &li.score[SC_TIME_BONUS], 1
248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
249 &li.auto_exit_sokoban, FALSE
254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
255 &li.auto_count_gems, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
261 &li.solved_by_one_player, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
267 &li.time_score_base, 1
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
273 &li.rate_time_over_score, FALSE
283 static struct LevelFileConfigInfo chunk_config_ELEM[] =
285 // (these values are the same for each player)
288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
289 &li.block_last_field, FALSE // default case for EM levels
293 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
294 &li.sp_block_last_field, TRUE // default case for SP levels
298 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
299 &li.instant_relocation, FALSE
303 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
304 &li.can_pass_to_walkable, FALSE
308 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
309 &li.block_snap_field, TRUE
313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
314 &li.continuous_snapping, TRUE
318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
319 &li.shifted_relocation, FALSE
323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
324 &li.lazy_relocation, FALSE
328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
329 &li.finish_dig_collect, TRUE
333 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
334 &li.keep_walkable_ce, FALSE
337 // (these values are different for each player)
340 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
341 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
346 &li.initial_player_gravity[0], FALSE
350 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
351 &li.use_start_element[0], FALSE
355 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
356 &li.start_element[0], EL_PLAYER_1
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
361 &li.use_artwork_element[0], FALSE
365 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
366 &li.artwork_element[0], EL_PLAYER_1
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
371 &li.use_explosion_element[0], FALSE
375 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
376 &li.explosion_element[0], EL_PLAYER_1
380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
381 &li.use_initial_inventory[0], FALSE
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
386 &li.initial_inventory_size[0], 1
390 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
391 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
392 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
397 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
398 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
403 &li.initial_player_gravity[1], FALSE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
408 &li.use_start_element[1], FALSE
412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
413 &li.start_element[1], EL_PLAYER_2
417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
418 &li.use_artwork_element[1], FALSE
422 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
423 &li.artwork_element[1], EL_PLAYER_2
427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
428 &li.use_explosion_element[1], FALSE
432 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
433 &li.explosion_element[1], EL_PLAYER_2
437 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
438 &li.use_initial_inventory[1], FALSE
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
443 &li.initial_inventory_size[1], 1
447 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
448 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
449 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
454 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
455 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
460 &li.initial_player_gravity[2], FALSE
464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
465 &li.use_start_element[2], FALSE
469 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
470 &li.start_element[2], EL_PLAYER_3
474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
475 &li.use_artwork_element[2], FALSE
479 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
480 &li.artwork_element[2], EL_PLAYER_3
484 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
485 &li.use_explosion_element[2], FALSE
489 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
490 &li.explosion_element[2], EL_PLAYER_3
494 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
495 &li.use_initial_inventory[2], FALSE
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
500 &li.initial_inventory_size[2], 1
504 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
505 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
506 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
511 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
512 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
517 &li.initial_player_gravity[3], FALSE
521 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
522 &li.use_start_element[3], FALSE
526 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
527 &li.start_element[3], EL_PLAYER_4
531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
532 &li.use_artwork_element[3], FALSE
536 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
537 &li.artwork_element[3], EL_PLAYER_4
541 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
542 &li.use_explosion_element[3], FALSE
546 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
547 &li.explosion_element[3], EL_PLAYER_4
551 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
552 &li.use_initial_inventory[3], FALSE
556 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
557 &li.initial_inventory_size[3], 1
561 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
562 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
563 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
568 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
569 &li.score[SC_EMERALD], 10
574 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
575 &li.score[SC_DIAMOND], 10
580 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
581 &li.score[SC_BUG], 10
586 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
587 &li.score[SC_SPACESHIP], 10
592 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
593 &li.score[SC_PACMAN], 10
598 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
599 &li.score[SC_NUT], 10
604 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
605 &li.score[SC_DYNAMITE], 10
610 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
611 &li.score[SC_KEY], 10
616 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
617 &li.score[SC_PEARL], 10
622 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
623 &li.score[SC_CRYSTAL], 10
628 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
629 &li.amoeba_content, EL_DIAMOND
633 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
638 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
639 &li.grow_into_diggable, TRUE
644 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
645 &li.yamyam_content, EL_ROCK, NULL,
646 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
650 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
651 &li.score[SC_YAMYAM], 10
656 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
657 &li.score[SC_ROBOT], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.time_magic_wall, 10
679 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
680 &li.game_of_life[0], 2
684 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
685 &li.game_of_life[1], 3
689 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
690 &li.game_of_life[2], 3
694 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
695 &li.game_of_life[3], 3
699 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
700 &li.use_life_bugs, FALSE
705 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
710 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
715 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
720 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
725 EL_TIMEGATE_SWITCH, -1,
726 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 &li.time_timegate, 10
731 EL_LIGHT_SWITCH_ACTIVE, -1,
732 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
737 EL_SHIELD_NORMAL, -1,
738 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 &li.shield_normal_time, 10
742 EL_SHIELD_NORMAL, -1,
743 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
744 &li.score[SC_SHIELD], 10
748 EL_SHIELD_DEADLY, -1,
749 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
750 &li.shield_deadly_time, 10
753 EL_SHIELD_DEADLY, -1,
754 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
755 &li.score[SC_SHIELD], 10
760 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
765 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
766 &li.extra_time_score, 10
770 EL_TIME_ORB_FULL, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 &li.time_orb_time, 10
775 EL_TIME_ORB_FULL, -1,
776 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
777 &li.use_time_orb_bug, FALSE
782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
783 &li.use_spring_bug, FALSE
788 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
789 &li.android_move_time, 10
793 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
794 &li.android_clone_time, 10
797 EL_EMC_ANDROID, SAVE_CONF_NEVER,
798 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
799 &li.android_clone_element[0], EL_EMPTY, NULL,
800 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
804 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
805 &li.android_clone_element[0], EL_EMPTY, NULL,
806 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
811 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
816 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
821 EL_EMC_MAGNIFIER, -1,
822 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
823 &li.magnify_score, 10
826 EL_EMC_MAGNIFIER, -1,
827 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
832 EL_EMC_MAGIC_BALL, -1,
833 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
837 EL_EMC_MAGIC_BALL, -1,
838 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
839 &li.ball_random, FALSE
842 EL_EMC_MAGIC_BALL, -1,
843 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
844 &li.ball_active_initial, FALSE
847 EL_EMC_MAGIC_BALL, -1,
848 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
849 &li.ball_content, EL_EMPTY, NULL,
850 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
854 EL_SOKOBAN_FIELD_EMPTY, -1,
855 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
856 &li.sb_fields_needed, TRUE
860 EL_SOKOBAN_OBJECT, -1,
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
862 &li.sb_objects_needed, TRUE
867 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
868 &li.mm_laser_red, FALSE
872 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
873 &li.mm_laser_green, FALSE
877 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
878 &li.mm_laser_blue, TRUE
883 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
884 &li.df_laser_red, TRUE
888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
889 &li.df_laser_green, TRUE
893 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
894 &li.df_laser_blue, FALSE
898 EL_MM_FUSE_ACTIVE, -1,
899 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
904 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
916 &li.mm_ball_choice_mode, ANIM_RANDOM
920 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
921 &li.mm_ball_content, EL_EMPTY, NULL,
922 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
926 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
927 &li.rotate_mm_ball_content, TRUE
931 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
932 &li.explode_mm_ball, FALSE
936 EL_MM_STEEL_BLOCK, -1,
937 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
938 &li.mm_time_block, 75
942 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
943 &li.score[SC_ELEM_BONUS], 10
946 // ---------- unused values -------------------------------------------------
949 EL_UNKNOWN, SAVE_CONF_NEVER,
950 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
951 &li.score[SC_UNKNOWN_15], 10
961 static struct LevelFileConfigInfo chunk_config_NOTE[] =
965 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
966 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
970 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
971 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
977 &xx_envelope.autowrap, FALSE
981 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
982 &xx_envelope.centered, FALSE
987 TYPE_STRING, CONF_VALUE_BYTES(1),
988 &xx_envelope.text, -1, NULL,
989 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
990 &xx_default_string_empty[0]
1000 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1004 TYPE_STRING, CONF_VALUE_BYTES(1),
1005 &xx_ei.description[0], -1,
1006 &yy_ei.description[0],
1007 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1008 &xx_default_description[0]
1013 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1014 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1015 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1017 #if ENABLE_RESERVED_CODE
1018 // (reserved for later use)
1021 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1022 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1023 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1029 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1030 &xx_ei.use_gfx_element, FALSE,
1031 &yy_ei.use_gfx_element
1035 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1036 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1037 &yy_ei.gfx_element_initial
1042 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1043 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1044 &yy_ei.access_direction
1049 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1050 &xx_ei.collect_score_initial, 10,
1051 &yy_ei.collect_score_initial
1055 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1056 &xx_ei.collect_count_initial, 1,
1057 &yy_ei.collect_count_initial
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1063 &xx_ei.ce_value_fixed_initial, 0,
1064 &yy_ei.ce_value_fixed_initial
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1069 &xx_ei.ce_value_random_initial, 0,
1070 &yy_ei.ce_value_random_initial
1074 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1075 &xx_ei.use_last_ce_value, FALSE,
1076 &yy_ei.use_last_ce_value
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1082 &xx_ei.push_delay_fixed, 8,
1083 &yy_ei.push_delay_fixed
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1088 &xx_ei.push_delay_random, 8,
1089 &yy_ei.push_delay_random
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1094 &xx_ei.drop_delay_fixed, 0,
1095 &yy_ei.drop_delay_fixed
1099 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1100 &xx_ei.drop_delay_random, 0,
1101 &yy_ei.drop_delay_random
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1106 &xx_ei.move_delay_fixed, 0,
1107 &yy_ei.move_delay_fixed
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1112 &xx_ei.move_delay_random, 0,
1113 &yy_ei.move_delay_random
1117 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1118 &xx_ei.step_delay_fixed, 0,
1119 &yy_ei.step_delay_fixed
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1124 &xx_ei.step_delay_random, 0,
1125 &yy_ei.step_delay_random
1130 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1131 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1136 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1137 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1138 &yy_ei.move_direction_initial
1142 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1143 &xx_ei.move_stepsize, TILEX / 8,
1144 &yy_ei.move_stepsize
1149 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1150 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1151 &yy_ei.move_enter_element
1155 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1156 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1157 &yy_ei.move_leave_element
1161 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1162 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1163 &yy_ei.move_leave_type
1168 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1169 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1170 &yy_ei.slippery_type
1175 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1176 &xx_ei.explosion_type, EXPLODES_3X3,
1177 &yy_ei.explosion_type
1181 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1182 &xx_ei.explosion_delay, 16,
1183 &yy_ei.explosion_delay
1187 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1188 &xx_ei.ignition_delay, 8,
1189 &yy_ei.ignition_delay
1194 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1195 &xx_ei.content, EL_EMPTY_SPACE,
1197 &xx_num_contents, 1, 1
1200 // ---------- "num_change_pages" must be the last entry ---------------------
1203 -1, SAVE_CONF_ALWAYS,
1204 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1205 &xx_ei.num_change_pages, 1,
1206 &yy_ei.num_change_pages
1217 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1219 // ---------- "current_change_page" must be the first entry -----------------
1222 -1, SAVE_CONF_ALWAYS,
1223 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1224 &xx_current_change_page, -1
1227 // ---------- (the remaining entries can be in any order) -------------------
1231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1232 &xx_change.can_change, FALSE
1237 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1238 &xx_event_bits[0], 0
1242 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1243 &xx_event_bits[1], 0
1248 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1249 &xx_change.trigger_player, CH_PLAYER_ANY
1253 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1254 &xx_change.trigger_side, CH_SIDE_ANY
1258 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1259 &xx_change.trigger_page, CH_PAGE_ANY
1264 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1265 &xx_change.target_element, EL_EMPTY_SPACE
1270 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1271 &xx_change.delay_fixed, 0
1275 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1276 &xx_change.delay_random, 0
1280 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1281 &xx_change.delay_frames, FRAMES_PER_SECOND
1286 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1287 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1293 &xx_change.explode, FALSE
1297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1298 &xx_change.use_target_content, FALSE
1302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1303 &xx_change.only_if_complete, FALSE
1307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1308 &xx_change.use_random_replace, FALSE
1312 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1313 &xx_change.random_percentage, 100
1317 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1318 &xx_change.replace_when, CP_WHEN_EMPTY
1323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1324 &xx_change.has_action, FALSE
1328 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1329 &xx_change.action_type, CA_NO_ACTION
1333 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1334 &xx_change.action_mode, CA_MODE_UNDEFINED
1338 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1339 &xx_change.action_arg, CA_ARG_UNDEFINED
1344 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1345 &xx_change.action_element, EL_EMPTY_SPACE
1350 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1351 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1352 &xx_num_contents, 1, 1
1362 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1366 TYPE_STRING, CONF_VALUE_BYTES(1),
1367 &xx_ei.description[0], -1, NULL,
1368 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1369 &xx_default_description[0]
1374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1375 &xx_ei.use_gfx_element, FALSE
1379 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1380 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1385 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1386 &xx_group.choice_mode, ANIM_RANDOM
1391 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1392 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1393 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1403 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1408 &xx_ei.use_gfx_element, FALSE
1412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1413 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1423 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1428 &li.block_snap_field, TRUE
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1433 &li.continuous_snapping, TRUE
1437 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1438 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1443 &li.use_start_element[0], FALSE
1447 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1448 &li.start_element[0], EL_PLAYER_1
1452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1453 &li.use_artwork_element[0], FALSE
1457 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1458 &li.artwork_element[0], EL_PLAYER_1
1462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1463 &li.use_explosion_element[0], FALSE
1467 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1468 &li.explosion_element[0], EL_PLAYER_1
1483 filetype_id_list[] =
1485 { LEVEL_FILE_TYPE_RND, "RND" },
1486 { LEVEL_FILE_TYPE_BD, "BD" },
1487 { LEVEL_FILE_TYPE_EM, "EM" },
1488 { LEVEL_FILE_TYPE_SP, "SP" },
1489 { LEVEL_FILE_TYPE_DX, "DX" },
1490 { LEVEL_FILE_TYPE_SB, "SB" },
1491 { LEVEL_FILE_TYPE_DC, "DC" },
1492 { LEVEL_FILE_TYPE_MM, "MM" },
1493 { LEVEL_FILE_TYPE_MM, "DF" },
1498 // ============================================================================
1499 // level file functions
1500 // ============================================================================
1502 static boolean check_special_flags(char *flag)
1504 if (strEqual(options.special_flags, flag) ||
1505 strEqual(leveldir_current->special_flags, flag))
1511 static struct DateInfo getCurrentDate(void)
1513 time_t epoch_seconds = time(NULL);
1514 struct tm *now = localtime(&epoch_seconds);
1515 struct DateInfo date;
1517 date.year = now->tm_year + 1900;
1518 date.month = now->tm_mon + 1;
1519 date.day = now->tm_mday;
1521 date.src = DATE_SRC_CLOCK;
1526 static void resetEventFlags(struct ElementChangeInfo *change)
1530 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1531 change->has_event[i] = FALSE;
1534 static void resetEventBits(void)
1538 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1539 xx_event_bits[i] = 0;
1542 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1546 /* important: only change event flag if corresponding event bit is set
1547 (this is because all xx_event_bits[] values are loaded separately,
1548 and all xx_event_bits[] values are set back to zero before loading
1549 another value xx_event_bits[x] (each value representing 32 flags)) */
1551 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1552 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1553 change->has_event[i] = TRUE;
1556 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1560 /* in contrast to the above function setEventFlagsFromEventBits(), it
1561 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1562 depending on the corresponding change->has_event[i] values here, as
1563 all xx_event_bits[] values are reset in resetEventBits() before */
1565 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1566 if (change->has_event[i])
1567 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1570 static char *getDefaultElementDescription(struct ElementInfo *ei)
1572 static char description[MAX_ELEMENT_NAME_LEN + 1];
1573 char *default_description = (ei->custom_description != NULL ?
1574 ei->custom_description :
1575 ei->editor_description);
1578 // always start with reliable default values
1579 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1580 description[i] = '\0';
1582 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1583 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1585 return &description[0];
1588 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1590 char *default_description = getDefaultElementDescription(ei);
1593 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1594 ei->description[i] = default_description[i];
1597 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1601 for (i = 0; conf[i].data_type != -1; i++)
1603 int default_value = conf[i].default_value;
1604 int data_type = conf[i].data_type;
1605 int conf_type = conf[i].conf_type;
1606 int byte_mask = conf_type & CONF_MASK_BYTES;
1608 if (byte_mask == CONF_MASK_MULTI_BYTES)
1610 int default_num_entities = conf[i].default_num_entities;
1611 int max_num_entities = conf[i].max_num_entities;
1613 *(int *)(conf[i].num_entities) = default_num_entities;
1615 if (data_type == TYPE_STRING)
1617 char *default_string = conf[i].default_string;
1618 char *string = (char *)(conf[i].value);
1620 strncpy(string, default_string, max_num_entities);
1622 else if (data_type == TYPE_ELEMENT_LIST)
1624 int *element_array = (int *)(conf[i].value);
1627 for (j = 0; j < max_num_entities; j++)
1628 element_array[j] = default_value;
1630 else if (data_type == TYPE_CONTENT_LIST)
1632 struct Content *content = (struct Content *)(conf[i].value);
1635 for (c = 0; c < max_num_entities; c++)
1636 for (y = 0; y < 3; y++)
1637 for (x = 0; x < 3; x++)
1638 content[c].e[x][y] = default_value;
1641 else // constant size configuration data (1, 2 or 4 bytes)
1643 if (data_type == TYPE_BOOLEAN)
1644 *(boolean *)(conf[i].value) = default_value;
1646 *(int *) (conf[i].value) = default_value;
1651 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1655 for (i = 0; conf[i].data_type != -1; i++)
1657 int data_type = conf[i].data_type;
1658 int conf_type = conf[i].conf_type;
1659 int byte_mask = conf_type & CONF_MASK_BYTES;
1661 if (byte_mask == CONF_MASK_MULTI_BYTES)
1663 int max_num_entities = conf[i].max_num_entities;
1665 if (data_type == TYPE_STRING)
1667 char *string = (char *)(conf[i].value);
1668 char *string_copy = (char *)(conf[i].value_copy);
1670 strncpy(string_copy, string, max_num_entities);
1672 else if (data_type == TYPE_ELEMENT_LIST)
1674 int *element_array = (int *)(conf[i].value);
1675 int *element_array_copy = (int *)(conf[i].value_copy);
1678 for (j = 0; j < max_num_entities; j++)
1679 element_array_copy[j] = element_array[j];
1681 else if (data_type == TYPE_CONTENT_LIST)
1683 struct Content *content = (struct Content *)(conf[i].value);
1684 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1687 for (c = 0; c < max_num_entities; c++)
1688 for (y = 0; y < 3; y++)
1689 for (x = 0; x < 3; x++)
1690 content_copy[c].e[x][y] = content[c].e[x][y];
1693 else // constant size configuration data (1, 2 or 4 bytes)
1695 if (data_type == TYPE_BOOLEAN)
1696 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1698 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1703 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1707 xx_ei = *ei_from; // copy element data into temporary buffer
1708 yy_ei = *ei_to; // copy element data into temporary buffer
1710 copyConfigFromConfigList(chunk_config_CUSX_base);
1715 // ---------- reinitialize and copy change pages ----------
1717 ei_to->num_change_pages = ei_from->num_change_pages;
1718 ei_to->current_change_page = ei_from->current_change_page;
1720 setElementChangePages(ei_to, ei_to->num_change_pages);
1722 for (i = 0; i < ei_to->num_change_pages; i++)
1723 ei_to->change_page[i] = ei_from->change_page[i];
1725 // ---------- copy group element info ----------
1726 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1727 *ei_to->group = *ei_from->group;
1729 // mark this custom element as modified
1730 ei_to->modified_settings = TRUE;
1733 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1735 int change_page_size = sizeof(struct ElementChangeInfo);
1737 ei->num_change_pages = MAX(1, change_pages);
1740 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1742 if (ei->current_change_page >= ei->num_change_pages)
1743 ei->current_change_page = ei->num_change_pages - 1;
1745 ei->change = &ei->change_page[ei->current_change_page];
1748 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1750 xx_change = *change; // copy change data into temporary buffer
1752 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1754 *change = xx_change;
1756 resetEventFlags(change);
1758 change->direct_action = 0;
1759 change->other_action = 0;
1761 change->pre_change_function = NULL;
1762 change->change_function = NULL;
1763 change->post_change_function = NULL;
1766 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1770 li = *level; // copy level data into temporary buffer
1771 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1772 *level = li; // copy temporary buffer back to level data
1774 setLevelInfoToDefaults_BD();
1775 setLevelInfoToDefaults_EM();
1776 setLevelInfoToDefaults_SP();
1777 setLevelInfoToDefaults_MM();
1779 level->native_bd_level = &native_bd_level;
1780 level->native_em_level = &native_em_level;
1781 level->native_sp_level = &native_sp_level;
1782 level->native_mm_level = &native_mm_level;
1784 level->file_version = FILE_VERSION_ACTUAL;
1785 level->game_version = GAME_VERSION_ACTUAL;
1787 level->creation_date = getCurrentDate();
1789 level->encoding_16bit_field = TRUE;
1790 level->encoding_16bit_yamyam = TRUE;
1791 level->encoding_16bit_amoeba = TRUE;
1793 // clear level name and level author string buffers
1794 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1795 level->name[i] = '\0';
1796 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1797 level->author[i] = '\0';
1799 // set level name and level author to default values
1800 strcpy(level->name, NAMELESS_LEVEL_NAME);
1801 strcpy(level->author, ANONYMOUS_NAME);
1803 // set level playfield to playable default level with player and exit
1804 for (x = 0; x < MAX_LEV_FIELDX; x++)
1805 for (y = 0; y < MAX_LEV_FIELDY; y++)
1806 level->field[x][y] = EL_SAND;
1808 level->field[0][0] = EL_PLAYER_1;
1809 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1811 BorderElement = EL_STEELWALL;
1813 // detect custom elements when loading them
1814 level->file_has_custom_elements = FALSE;
1816 // set all bug compatibility flags to "false" => do not emulate this bug
1817 level->use_action_after_change_bug = FALSE;
1819 if (leveldir_current)
1821 // try to determine better author name than 'anonymous'
1822 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1824 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1825 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1829 switch (LEVELCLASS(leveldir_current))
1831 case LEVELCLASS_TUTORIAL:
1832 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1835 case LEVELCLASS_CONTRIB:
1836 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1837 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1840 case LEVELCLASS_PRIVATE:
1841 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1842 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1846 // keep default value
1853 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1855 static boolean clipboard_elements_initialized = FALSE;
1858 InitElementPropertiesStatic();
1860 li = *level; // copy level data into temporary buffer
1861 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1862 *level = li; // copy temporary buffer back to level data
1864 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1867 struct ElementInfo *ei = &element_info[element];
1869 if (element == EL_MM_GRAY_BALL)
1871 struct LevelInfo_MM *level_mm = level->native_mm_level;
1874 for (j = 0; j < level->num_mm_ball_contents; j++)
1875 level->mm_ball_content[j] =
1876 map_element_MM_to_RND(level_mm->ball_content[j]);
1879 // never initialize clipboard elements after the very first time
1880 // (to be able to use clipboard elements between several levels)
1881 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1884 if (IS_ENVELOPE(element))
1886 int envelope_nr = element - EL_ENVELOPE_1;
1888 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1890 level->envelope[envelope_nr] = xx_envelope;
1893 if (IS_CUSTOM_ELEMENT(element) ||
1894 IS_GROUP_ELEMENT(element) ||
1895 IS_INTERNAL_ELEMENT(element))
1897 xx_ei = *ei; // copy element data into temporary buffer
1899 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1904 setElementChangePages(ei, 1);
1905 setElementChangeInfoToDefaults(ei->change);
1907 if (IS_CUSTOM_ELEMENT(element) ||
1908 IS_GROUP_ELEMENT(element))
1910 setElementDescriptionToDefault(ei);
1912 ei->modified_settings = FALSE;
1915 if (IS_CUSTOM_ELEMENT(element) ||
1916 IS_INTERNAL_ELEMENT(element))
1918 // internal values used in level editor
1920 ei->access_type = 0;
1921 ei->access_layer = 0;
1922 ei->access_protected = 0;
1923 ei->walk_to_action = 0;
1924 ei->smash_targets = 0;
1927 ei->can_explode_by_fire = FALSE;
1928 ei->can_explode_smashed = FALSE;
1929 ei->can_explode_impact = FALSE;
1931 ei->current_change_page = 0;
1934 if (IS_GROUP_ELEMENT(element) ||
1935 IS_INTERNAL_ELEMENT(element))
1937 struct ElementGroupInfo *group;
1939 // initialize memory for list of elements in group
1940 if (ei->group == NULL)
1941 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1945 xx_group = *group; // copy group data into temporary buffer
1947 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1952 if (IS_EMPTY_ELEMENT(element) ||
1953 IS_INTERNAL_ELEMENT(element))
1955 xx_ei = *ei; // copy element data into temporary buffer
1957 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1963 clipboard_elements_initialized = TRUE;
1966 static void setLevelInfoToDefaults(struct LevelInfo *level,
1967 boolean level_info_only,
1968 boolean reset_file_status)
1970 setLevelInfoToDefaults_Level(level);
1972 if (!level_info_only)
1973 setLevelInfoToDefaults_Elements(level);
1975 if (reset_file_status)
1977 level->no_valid_file = FALSE;
1978 level->no_level_file = FALSE;
1981 level->changed = FALSE;
1984 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1986 level_file_info->nr = 0;
1987 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1988 level_file_info->packed = FALSE;
1990 setString(&level_file_info->basename, NULL);
1991 setString(&level_file_info->filename, NULL);
1994 int getMappedElement_SB(int, boolean);
1996 static void ActivateLevelTemplate(void)
2000 if (check_special_flags("load_xsb_to_ces"))
2002 // fill smaller playfields with padding "beyond border wall" elements
2003 if (level.fieldx < level_template.fieldx ||
2004 level.fieldy < level_template.fieldy)
2006 short field[level.fieldx][level.fieldy];
2007 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2008 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2009 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2010 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2012 // copy old playfield (which is smaller than the visible area)
2013 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2014 field[x][y] = level.field[x][y];
2016 // fill new, larger playfield with "beyond border wall" elements
2017 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2018 level.field[x][y] = getMappedElement_SB('_', TRUE);
2020 // copy the old playfield to the middle of the new playfield
2021 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2022 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2024 level.fieldx = new_fieldx;
2025 level.fieldy = new_fieldy;
2029 // Currently there is no special action needed to activate the template
2030 // data, because 'element_info' property settings overwrite the original
2031 // level data, while all other variables do not change.
2033 // Exception: 'from_level_template' elements in the original level playfield
2034 // are overwritten with the corresponding elements at the same position in
2035 // playfield from the level template.
2037 for (x = 0; x < level.fieldx; x++)
2038 for (y = 0; y < level.fieldy; y++)
2039 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2040 level.field[x][y] = level_template.field[x][y];
2042 if (check_special_flags("load_xsb_to_ces"))
2044 struct LevelInfo level_backup = level;
2046 // overwrite all individual level settings from template level settings
2047 level = level_template;
2049 // restore level file info
2050 level.file_info = level_backup.file_info;
2052 // restore playfield size
2053 level.fieldx = level_backup.fieldx;
2054 level.fieldy = level_backup.fieldy;
2056 // restore playfield content
2057 for (x = 0; x < level.fieldx; x++)
2058 for (y = 0; y < level.fieldy; y++)
2059 level.field[x][y] = level_backup.field[x][y];
2061 // restore name and author from individual level
2062 strcpy(level.name, level_backup.name);
2063 strcpy(level.author, level_backup.author);
2065 // restore flag "use_custom_template"
2066 level.use_custom_template = level_backup.use_custom_template;
2070 static char *getLevelFilenameFromBasename(char *basename)
2072 static char *filename = NULL;
2074 checked_free(filename);
2076 filename = getPath2(getCurrentLevelDir(), basename);
2081 static int getFileTypeFromBasename(char *basename)
2083 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2085 static char *filename = NULL;
2086 struct stat file_status;
2088 // ---------- try to determine file type from filename ----------
2090 // check for typical filename of a Supaplex level package file
2091 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2092 return LEVEL_FILE_TYPE_SP;
2094 // check for typical filename of a Diamond Caves II level package file
2095 if (strSuffixLower(basename, ".dc") ||
2096 strSuffixLower(basename, ".dc2"))
2097 return LEVEL_FILE_TYPE_DC;
2099 // check for typical filename of a Sokoban level package file
2100 if (strSuffixLower(basename, ".xsb") &&
2101 strchr(basename, '%') == NULL)
2102 return LEVEL_FILE_TYPE_SB;
2104 // ---------- try to determine file type from filesize ----------
2106 checked_free(filename);
2107 filename = getPath2(getCurrentLevelDir(), basename);
2109 if (stat(filename, &file_status) == 0)
2111 // check for typical filesize of a Supaplex level package file
2112 if (file_status.st_size == 170496)
2113 return LEVEL_FILE_TYPE_SP;
2116 return LEVEL_FILE_TYPE_UNKNOWN;
2119 static int getFileTypeFromMagicBytes(char *filename, int type)
2123 if ((file = openFile(filename, MODE_READ)))
2125 char chunk_name[CHUNK_ID_LEN + 1];
2127 getFileChunkBE(file, chunk_name, NULL);
2129 if (strEqual(chunk_name, "MMII") ||
2130 strEqual(chunk_name, "MIRR"))
2131 type = LEVEL_FILE_TYPE_MM;
2139 static boolean checkForPackageFromBasename(char *basename)
2141 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2142 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2144 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2147 static char *getSingleLevelBasenameExt(int nr, char *extension)
2149 static char basename[MAX_FILENAME_LEN];
2152 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2154 sprintf(basename, "%03d.%s", nr, extension);
2159 static char *getSingleLevelBasename(int nr)
2161 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2164 static char *getPackedLevelBasename(int type)
2166 static char basename[MAX_FILENAME_LEN];
2167 char *directory = getCurrentLevelDir();
2169 DirectoryEntry *dir_entry;
2171 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2173 if ((dir = openDirectory(directory)) == NULL)
2175 Warn("cannot read current level directory '%s'", directory);
2180 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2182 char *entry_basename = dir_entry->basename;
2183 int entry_type = getFileTypeFromBasename(entry_basename);
2185 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2187 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2190 strcpy(basename, entry_basename);
2197 closeDirectory(dir);
2202 static char *getSingleLevelFilename(int nr)
2204 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2207 #if ENABLE_UNUSED_CODE
2208 static char *getPackedLevelFilename(int type)
2210 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2214 char *getDefaultLevelFilename(int nr)
2216 return getSingleLevelFilename(nr);
2219 #if ENABLE_UNUSED_CODE
2220 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2224 lfi->packed = FALSE;
2226 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2227 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2231 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2232 int type, char *format, ...)
2234 static char basename[MAX_FILENAME_LEN];
2237 va_start(ap, format);
2238 vsprintf(basename, format, ap);
2242 lfi->packed = FALSE;
2244 setString(&lfi->basename, basename);
2245 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2248 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2254 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2255 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2258 static int getFiletypeFromID(char *filetype_id)
2260 char *filetype_id_lower;
2261 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2264 if (filetype_id == NULL)
2265 return LEVEL_FILE_TYPE_UNKNOWN;
2267 filetype_id_lower = getStringToLower(filetype_id);
2269 for (i = 0; filetype_id_list[i].id != NULL; i++)
2271 char *id_lower = getStringToLower(filetype_id_list[i].id);
2273 if (strEqual(filetype_id_lower, id_lower))
2274 filetype = filetype_id_list[i].filetype;
2278 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2282 free(filetype_id_lower);
2287 char *getLocalLevelTemplateFilename(void)
2289 return getDefaultLevelFilename(-1);
2292 char *getGlobalLevelTemplateFilename(void)
2294 // global variable "leveldir_current" must be modified in the loop below
2295 LevelDirTree *leveldir_current_last = leveldir_current;
2296 char *filename = NULL;
2298 // check for template level in path from current to topmost tree node
2300 while (leveldir_current != NULL)
2302 filename = getDefaultLevelFilename(-1);
2304 if (fileExists(filename))
2307 leveldir_current = leveldir_current->node_parent;
2310 // restore global variable "leveldir_current" modified in above loop
2311 leveldir_current = leveldir_current_last;
2316 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2320 // special case: level number is negative => check for level template file
2323 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2324 getSingleLevelBasename(-1));
2326 // replace local level template filename with global template filename
2327 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2329 // no fallback if template file not existing
2333 // special case: check for file name/pattern specified in "levelinfo.conf"
2334 if (leveldir_current->level_filename != NULL)
2336 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2338 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2339 leveldir_current->level_filename, nr);
2341 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2343 if (fileExists(lfi->filename))
2346 else if (leveldir_current->level_filetype != NULL)
2348 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2350 // check for specified native level file with standard file name
2351 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2352 "%03d.%s", nr, LEVELFILE_EXTENSION);
2353 if (fileExists(lfi->filename))
2357 // check for native Rocks'n'Diamonds level file
2358 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2359 "%03d.%s", nr, LEVELFILE_EXTENSION);
2360 if (fileExists(lfi->filename))
2363 // check for Emerald Mine level file (V1)
2364 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2365 'a' + (nr / 10) % 26, '0' + nr % 10);
2366 if (fileExists(lfi->filename))
2368 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2369 'A' + (nr / 10) % 26, '0' + nr % 10);
2370 if (fileExists(lfi->filename))
2373 // check for Emerald Mine level file (V2 to V5)
2374 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2375 if (fileExists(lfi->filename))
2378 // check for Emerald Mine level file (V6 / single mode)
2379 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2380 if (fileExists(lfi->filename))
2382 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2383 if (fileExists(lfi->filename))
2386 // check for Emerald Mine level file (V6 / teamwork mode)
2387 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2388 if (fileExists(lfi->filename))
2390 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2391 if (fileExists(lfi->filename))
2394 // check for various packed level file formats
2395 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2396 if (fileExists(lfi->filename))
2399 // no known level file found -- use default values (and fail later)
2400 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2401 "%03d.%s", nr, LEVELFILE_EXTENSION);
2404 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2406 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2407 lfi->type = getFileTypeFromBasename(lfi->basename);
2409 if (lfi->type == LEVEL_FILE_TYPE_RND)
2410 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2413 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2415 // always start with reliable default values
2416 setFileInfoToDefaults(level_file_info);
2418 level_file_info->nr = nr; // set requested level number
2420 determineLevelFileInfo_Filename(level_file_info);
2421 determineLevelFileInfo_Filetype(level_file_info);
2424 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2425 struct LevelFileInfo *lfi_to)
2427 lfi_to->nr = lfi_from->nr;
2428 lfi_to->type = lfi_from->type;
2429 lfi_to->packed = lfi_from->packed;
2431 setString(&lfi_to->basename, lfi_from->basename);
2432 setString(&lfi_to->filename, lfi_from->filename);
2435 // ----------------------------------------------------------------------------
2436 // functions for loading R'n'D level
2437 // ----------------------------------------------------------------------------
2439 int getMappedElement(int element)
2441 // remap some (historic, now obsolete) elements
2445 case EL_PLAYER_OBSOLETE:
2446 element = EL_PLAYER_1;
2449 case EL_KEY_OBSOLETE:
2453 case EL_EM_KEY_1_FILE_OBSOLETE:
2454 element = EL_EM_KEY_1;
2457 case EL_EM_KEY_2_FILE_OBSOLETE:
2458 element = EL_EM_KEY_2;
2461 case EL_EM_KEY_3_FILE_OBSOLETE:
2462 element = EL_EM_KEY_3;
2465 case EL_EM_KEY_4_FILE_OBSOLETE:
2466 element = EL_EM_KEY_4;
2469 case EL_ENVELOPE_OBSOLETE:
2470 element = EL_ENVELOPE_1;
2478 if (element >= NUM_FILE_ELEMENTS)
2480 Warn("invalid level element %d", element);
2482 element = EL_UNKNOWN;
2490 static int getMappedElementByVersion(int element, int game_version)
2492 // remap some elements due to certain game version
2494 if (game_version <= VERSION_IDENT(2,2,0,0))
2496 // map game font elements
2497 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2498 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2499 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2500 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2503 if (game_version < VERSION_IDENT(3,0,0,0))
2505 // map Supaplex gravity tube elements
2506 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2507 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2508 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2509 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2516 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2518 level->file_version = getFileVersion(file);
2519 level->game_version = getFileVersion(file);
2524 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2526 level->creation_date.year = getFile16BitBE(file);
2527 level->creation_date.month = getFile8Bit(file);
2528 level->creation_date.day = getFile8Bit(file);
2530 level->creation_date.src = DATE_SRC_LEVELFILE;
2535 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2537 int initial_player_stepsize;
2538 int initial_player_gravity;
2541 level->fieldx = getFile8Bit(file);
2542 level->fieldy = getFile8Bit(file);
2544 level->time = getFile16BitBE(file);
2545 level->gems_needed = getFile16BitBE(file);
2547 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2548 level->name[i] = getFile8Bit(file);
2549 level->name[MAX_LEVEL_NAME_LEN] = 0;
2551 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2552 level->score[i] = getFile8Bit(file);
2554 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2555 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2556 for (y = 0; y < 3; y++)
2557 for (x = 0; x < 3; x++)
2558 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2560 level->amoeba_speed = getFile8Bit(file);
2561 level->time_magic_wall = getFile8Bit(file);
2562 level->time_wheel = getFile8Bit(file);
2563 level->amoeba_content = getMappedElement(getFile8Bit(file));
2565 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2568 for (i = 0; i < MAX_PLAYERS; i++)
2569 level->initial_player_stepsize[i] = initial_player_stepsize;
2571 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2573 for (i = 0; i < MAX_PLAYERS; i++)
2574 level->initial_player_gravity[i] = initial_player_gravity;
2576 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2577 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2579 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2581 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2582 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2583 level->can_move_into_acid_bits = getFile32BitBE(file);
2584 level->dont_collide_with_bits = getFile8Bit(file);
2586 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2587 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2589 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2590 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2591 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2593 level->game_engine_type = getFile8Bit(file);
2595 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2600 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2604 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2605 level->name[i] = getFile8Bit(file);
2606 level->name[MAX_LEVEL_NAME_LEN] = 0;
2611 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2615 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2616 level->author[i] = getFile8Bit(file);
2617 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2622 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2625 int chunk_size_expected = level->fieldx * level->fieldy;
2627 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2628 stored with 16-bit encoding (and should be twice as big then).
2629 Even worse, playfield data was stored 16-bit when only yamyam content
2630 contained 16-bit elements and vice versa. */
2632 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2633 chunk_size_expected *= 2;
2635 if (chunk_size_expected != chunk_size)
2637 ReadUnusedBytesFromFile(file, chunk_size);
2638 return chunk_size_expected;
2641 for (y = 0; y < level->fieldy; y++)
2642 for (x = 0; x < level->fieldx; x++)
2643 level->field[x][y] =
2644 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2649 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2652 int header_size = 4;
2653 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2654 int chunk_size_expected = header_size + content_size;
2656 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2657 stored with 16-bit encoding (and should be twice as big then).
2658 Even worse, playfield data was stored 16-bit when only yamyam content
2659 contained 16-bit elements and vice versa. */
2661 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2662 chunk_size_expected += content_size;
2664 if (chunk_size_expected != chunk_size)
2666 ReadUnusedBytesFromFile(file, chunk_size);
2667 return chunk_size_expected;
2671 level->num_yamyam_contents = getFile8Bit(file);
2675 // correct invalid number of content fields -- should never happen
2676 if (level->num_yamyam_contents < 1 ||
2677 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2678 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2680 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2681 for (y = 0; y < 3; y++)
2682 for (x = 0; x < 3; x++)
2683 level->yamyam_content[i].e[x][y] =
2684 getMappedElement(level->encoding_16bit_field ?
2685 getFile16BitBE(file) : getFile8Bit(file));
2689 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2694 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2696 element = getMappedElement(getFile16BitBE(file));
2697 num_contents = getFile8Bit(file);
2699 getFile8Bit(file); // content x size (unused)
2700 getFile8Bit(file); // content y size (unused)
2702 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2704 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2705 for (y = 0; y < 3; y++)
2706 for (x = 0; x < 3; x++)
2707 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2709 // correct invalid number of content fields -- should never happen
2710 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2711 num_contents = STD_ELEMENT_CONTENTS;
2713 if (element == EL_YAMYAM)
2715 level->num_yamyam_contents = num_contents;
2717 for (i = 0; i < num_contents; i++)
2718 for (y = 0; y < 3; y++)
2719 for (x = 0; x < 3; x++)
2720 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2722 else if (element == EL_BD_AMOEBA)
2724 level->amoeba_content = content_array[0][0][0];
2728 Warn("cannot load content for element '%d'", element);
2734 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2740 int chunk_size_expected;
2742 element = getMappedElement(getFile16BitBE(file));
2743 if (!IS_ENVELOPE(element))
2744 element = EL_ENVELOPE_1;
2746 envelope_nr = element - EL_ENVELOPE_1;
2748 envelope_len = getFile16BitBE(file);
2750 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2751 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2753 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2755 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2756 if (chunk_size_expected != chunk_size)
2758 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2759 return chunk_size_expected;
2762 for (i = 0; i < envelope_len; i++)
2763 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2768 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2770 int num_changed_custom_elements = getFile16BitBE(file);
2771 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2774 if (chunk_size_expected != chunk_size)
2776 ReadUnusedBytesFromFile(file, chunk_size - 2);
2777 return chunk_size_expected;
2780 for (i = 0; i < num_changed_custom_elements; i++)
2782 int element = getMappedElement(getFile16BitBE(file));
2783 int properties = getFile32BitBE(file);
2785 if (IS_CUSTOM_ELEMENT(element))
2786 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2788 Warn("invalid custom element number %d", element);
2790 // older game versions that wrote level files with CUS1 chunks used
2791 // different default push delay values (not yet stored in level file)
2792 element_info[element].push_delay_fixed = 2;
2793 element_info[element].push_delay_random = 8;
2796 level->file_has_custom_elements = TRUE;
2801 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2803 int num_changed_custom_elements = getFile16BitBE(file);
2804 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2807 if (chunk_size_expected != chunk_size)
2809 ReadUnusedBytesFromFile(file, chunk_size - 2);
2810 return chunk_size_expected;
2813 for (i = 0; i < num_changed_custom_elements; i++)
2815 int element = getMappedElement(getFile16BitBE(file));
2816 int custom_target_element = getMappedElement(getFile16BitBE(file));
2818 if (IS_CUSTOM_ELEMENT(element))
2819 element_info[element].change->target_element = custom_target_element;
2821 Warn("invalid custom element number %d", element);
2824 level->file_has_custom_elements = TRUE;
2829 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2831 int num_changed_custom_elements = getFile16BitBE(file);
2832 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2835 if (chunk_size_expected != chunk_size)
2837 ReadUnusedBytesFromFile(file, chunk_size - 2);
2838 return chunk_size_expected;
2841 for (i = 0; i < num_changed_custom_elements; i++)
2843 int element = getMappedElement(getFile16BitBE(file));
2844 struct ElementInfo *ei = &element_info[element];
2845 unsigned int event_bits;
2847 if (!IS_CUSTOM_ELEMENT(element))
2849 Warn("invalid custom element number %d", element);
2851 element = EL_INTERNAL_DUMMY;
2854 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2855 ei->description[j] = getFile8Bit(file);
2856 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2858 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2860 // some free bytes for future properties and padding
2861 ReadUnusedBytesFromFile(file, 7);
2863 ei->use_gfx_element = getFile8Bit(file);
2864 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2866 ei->collect_score_initial = getFile8Bit(file);
2867 ei->collect_count_initial = getFile8Bit(file);
2869 ei->push_delay_fixed = getFile16BitBE(file);
2870 ei->push_delay_random = getFile16BitBE(file);
2871 ei->move_delay_fixed = getFile16BitBE(file);
2872 ei->move_delay_random = getFile16BitBE(file);
2874 ei->move_pattern = getFile16BitBE(file);
2875 ei->move_direction_initial = getFile8Bit(file);
2876 ei->move_stepsize = getFile8Bit(file);
2878 for (y = 0; y < 3; y++)
2879 for (x = 0; x < 3; x++)
2880 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2882 // bits 0 - 31 of "has_event[]"
2883 event_bits = getFile32BitBE(file);
2884 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2885 if (event_bits & (1u << j))
2886 ei->change->has_event[j] = TRUE;
2888 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2890 ei->change->delay_fixed = getFile16BitBE(file);
2891 ei->change->delay_random = getFile16BitBE(file);
2892 ei->change->delay_frames = getFile16BitBE(file);
2894 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2896 ei->change->explode = getFile8Bit(file);
2897 ei->change->use_target_content = getFile8Bit(file);
2898 ei->change->only_if_complete = getFile8Bit(file);
2899 ei->change->use_random_replace = getFile8Bit(file);
2901 ei->change->random_percentage = getFile8Bit(file);
2902 ei->change->replace_when = getFile8Bit(file);
2904 for (y = 0; y < 3; y++)
2905 for (x = 0; x < 3; x++)
2906 ei->change->target_content.e[x][y] =
2907 getMappedElement(getFile16BitBE(file));
2909 ei->slippery_type = getFile8Bit(file);
2911 // some free bytes for future properties and padding
2912 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2914 // mark that this custom element has been modified
2915 ei->modified_settings = TRUE;
2918 level->file_has_custom_elements = TRUE;
2923 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2925 struct ElementInfo *ei;
2926 int chunk_size_expected;
2930 // ---------- custom element base property values (96 bytes) ----------------
2932 element = getMappedElement(getFile16BitBE(file));
2934 if (!IS_CUSTOM_ELEMENT(element))
2936 Warn("invalid custom element number %d", element);
2938 ReadUnusedBytesFromFile(file, chunk_size - 2);
2943 ei = &element_info[element];
2945 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2946 ei->description[i] = getFile8Bit(file);
2947 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2949 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2951 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2953 ei->num_change_pages = getFile8Bit(file);
2955 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2956 if (chunk_size_expected != chunk_size)
2958 ReadUnusedBytesFromFile(file, chunk_size - 43);
2959 return chunk_size_expected;
2962 ei->ce_value_fixed_initial = getFile16BitBE(file);
2963 ei->ce_value_random_initial = getFile16BitBE(file);
2964 ei->use_last_ce_value = getFile8Bit(file);
2966 ei->use_gfx_element = getFile8Bit(file);
2967 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2969 ei->collect_score_initial = getFile8Bit(file);
2970 ei->collect_count_initial = getFile8Bit(file);
2972 ei->drop_delay_fixed = getFile8Bit(file);
2973 ei->push_delay_fixed = getFile8Bit(file);
2974 ei->drop_delay_random = getFile8Bit(file);
2975 ei->push_delay_random = getFile8Bit(file);
2976 ei->move_delay_fixed = getFile16BitBE(file);
2977 ei->move_delay_random = getFile16BitBE(file);
2979 // bits 0 - 15 of "move_pattern" ...
2980 ei->move_pattern = getFile16BitBE(file);
2981 ei->move_direction_initial = getFile8Bit(file);
2982 ei->move_stepsize = getFile8Bit(file);
2984 ei->slippery_type = getFile8Bit(file);
2986 for (y = 0; y < 3; y++)
2987 for (x = 0; x < 3; x++)
2988 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2990 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2991 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2992 ei->move_leave_type = getFile8Bit(file);
2994 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2995 ei->move_pattern |= (getFile16BitBE(file) << 16);
2997 ei->access_direction = getFile8Bit(file);
2999 ei->explosion_delay = getFile8Bit(file);
3000 ei->ignition_delay = getFile8Bit(file);
3001 ei->explosion_type = getFile8Bit(file);
3003 // some free bytes for future custom property values and padding
3004 ReadUnusedBytesFromFile(file, 1);
3006 // ---------- change page property values (48 bytes) ------------------------
3008 setElementChangePages(ei, ei->num_change_pages);
3010 for (i = 0; i < ei->num_change_pages; i++)
3012 struct ElementChangeInfo *change = &ei->change_page[i];
3013 unsigned int event_bits;
3015 // always start with reliable default values
3016 setElementChangeInfoToDefaults(change);
3018 // bits 0 - 31 of "has_event[]" ...
3019 event_bits = getFile32BitBE(file);
3020 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3021 if (event_bits & (1u << j))
3022 change->has_event[j] = TRUE;
3024 change->target_element = getMappedElement(getFile16BitBE(file));
3026 change->delay_fixed = getFile16BitBE(file);
3027 change->delay_random = getFile16BitBE(file);
3028 change->delay_frames = getFile16BitBE(file);
3030 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3032 change->explode = getFile8Bit(file);
3033 change->use_target_content = getFile8Bit(file);
3034 change->only_if_complete = getFile8Bit(file);
3035 change->use_random_replace = getFile8Bit(file);
3037 change->random_percentage = getFile8Bit(file);
3038 change->replace_when = getFile8Bit(file);
3040 for (y = 0; y < 3; y++)
3041 for (x = 0; x < 3; x++)
3042 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3044 change->can_change = getFile8Bit(file);
3046 change->trigger_side = getFile8Bit(file);
3048 change->trigger_player = getFile8Bit(file);
3049 change->trigger_page = getFile8Bit(file);
3051 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3052 CH_PAGE_ANY : (1 << change->trigger_page));
3054 change->has_action = getFile8Bit(file);
3055 change->action_type = getFile8Bit(file);
3056 change->action_mode = getFile8Bit(file);
3057 change->action_arg = getFile16BitBE(file);
3059 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3060 event_bits = getFile8Bit(file);
3061 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3062 if (event_bits & (1u << (j - 32)))
3063 change->has_event[j] = TRUE;
3066 // mark this custom element as modified
3067 ei->modified_settings = TRUE;
3069 level->file_has_custom_elements = TRUE;
3074 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3076 struct ElementInfo *ei;
3077 struct ElementGroupInfo *group;
3081 element = getMappedElement(getFile16BitBE(file));
3083 if (!IS_GROUP_ELEMENT(element))
3085 Warn("invalid group element number %d", element);
3087 ReadUnusedBytesFromFile(file, chunk_size - 2);
3092 ei = &element_info[element];
3094 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3095 ei->description[i] = getFile8Bit(file);
3096 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3098 group = element_info[element].group;
3100 group->num_elements = getFile8Bit(file);
3102 ei->use_gfx_element = getFile8Bit(file);
3103 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3105 group->choice_mode = getFile8Bit(file);
3107 // some free bytes for future values and padding
3108 ReadUnusedBytesFromFile(file, 3);
3110 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3111 group->element[i] = getMappedElement(getFile16BitBE(file));
3113 // mark this group element as modified
3114 element_info[element].modified_settings = TRUE;
3116 level->file_has_custom_elements = TRUE;
3121 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3122 int element, int real_element)
3124 int micro_chunk_size = 0;
3125 int conf_type = getFile8Bit(file);
3126 int byte_mask = conf_type & CONF_MASK_BYTES;
3127 boolean element_found = FALSE;
3130 micro_chunk_size += 1;
3132 if (byte_mask == CONF_MASK_MULTI_BYTES)
3134 int num_bytes = getFile16BitBE(file);
3135 byte *buffer = checked_malloc(num_bytes);
3137 ReadBytesFromFile(file, buffer, num_bytes);
3139 for (i = 0; conf[i].data_type != -1; i++)
3141 if (conf[i].element == element &&
3142 conf[i].conf_type == conf_type)
3144 int data_type = conf[i].data_type;
3145 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3146 int max_num_entities = conf[i].max_num_entities;
3148 if (num_entities > max_num_entities)
3150 Warn("truncating number of entities for element %d from %d to %d",
3151 element, num_entities, max_num_entities);
3153 num_entities = max_num_entities;
3156 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3157 data_type == TYPE_CONTENT_LIST))
3159 // for element and content lists, zero entities are not allowed
3160 Warn("found empty list of entities for element %d", element);
3162 // do not set "num_entities" here to prevent reading behind buffer
3164 *(int *)(conf[i].num_entities) = 1; // at least one is required
3168 *(int *)(conf[i].num_entities) = num_entities;
3171 element_found = TRUE;
3173 if (data_type == TYPE_STRING)
3175 char *string = (char *)(conf[i].value);
3178 for (j = 0; j < max_num_entities; j++)
3179 string[j] = (j < num_entities ? buffer[j] : '\0');
3181 else if (data_type == TYPE_ELEMENT_LIST)
3183 int *element_array = (int *)(conf[i].value);
3186 for (j = 0; j < num_entities; j++)
3188 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3190 else if (data_type == TYPE_CONTENT_LIST)
3192 struct Content *content= (struct Content *)(conf[i].value);
3195 for (c = 0; c < num_entities; c++)
3196 for (y = 0; y < 3; y++)
3197 for (x = 0; x < 3; x++)
3198 content[c].e[x][y] =
3199 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3202 element_found = FALSE;
3208 checked_free(buffer);
3210 micro_chunk_size += 2 + num_bytes;
3212 else // constant size configuration data (1, 2 or 4 bytes)
3214 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3215 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3216 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3218 for (i = 0; conf[i].data_type != -1; i++)
3220 if (conf[i].element == element &&
3221 conf[i].conf_type == conf_type)
3223 int data_type = conf[i].data_type;
3225 if (data_type == TYPE_ELEMENT)
3226 value = getMappedElement(value);
3228 if (data_type == TYPE_BOOLEAN)
3229 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3231 *(int *) (conf[i].value) = value;
3233 element_found = TRUE;
3239 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3244 char *error_conf_chunk_bytes =
3245 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3246 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3247 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3248 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3249 int error_element = real_element;
3251 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3252 error_conf_chunk_bytes, error_conf_chunk_token,
3253 error_element, EL_NAME(error_element));
3256 return micro_chunk_size;
3259 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3261 int real_chunk_size = 0;
3263 li = *level; // copy level data into temporary buffer
3265 while (!checkEndOfFile(file))
3267 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3269 if (real_chunk_size >= chunk_size)
3273 *level = li; // copy temporary buffer back to level data
3275 return real_chunk_size;
3278 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3280 int real_chunk_size = 0;
3282 li = *level; // copy level data into temporary buffer
3284 while (!checkEndOfFile(file))
3286 int element = getMappedElement(getFile16BitBE(file));
3288 real_chunk_size += 2;
3289 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3291 if (real_chunk_size >= chunk_size)
3295 *level = li; // copy temporary buffer back to level data
3297 return real_chunk_size;
3300 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3302 int real_chunk_size = 0;
3304 li = *level; // copy level data into temporary buffer
3306 while (!checkEndOfFile(file))
3308 int element = getMappedElement(getFile16BitBE(file));
3310 real_chunk_size += 2;
3311 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3313 if (real_chunk_size >= chunk_size)
3317 *level = li; // copy temporary buffer back to level data
3319 return real_chunk_size;
3322 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3324 int element = getMappedElement(getFile16BitBE(file));
3325 int envelope_nr = element - EL_ENVELOPE_1;
3326 int real_chunk_size = 2;
3328 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3330 while (!checkEndOfFile(file))
3332 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3335 if (real_chunk_size >= chunk_size)
3339 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3341 return real_chunk_size;
3344 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3346 int element = getMappedElement(getFile16BitBE(file));
3347 int real_chunk_size = 2;
3348 struct ElementInfo *ei = &element_info[element];
3351 xx_ei = *ei; // copy element data into temporary buffer
3353 xx_ei.num_change_pages = -1;
3355 while (!checkEndOfFile(file))
3357 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3359 if (xx_ei.num_change_pages != -1)
3362 if (real_chunk_size >= chunk_size)
3368 if (ei->num_change_pages == -1)
3370 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3373 ei->num_change_pages = 1;
3375 setElementChangePages(ei, 1);
3376 setElementChangeInfoToDefaults(ei->change);
3378 return real_chunk_size;
3381 // initialize number of change pages stored for this custom element
3382 setElementChangePages(ei, ei->num_change_pages);
3383 for (i = 0; i < ei->num_change_pages; i++)
3384 setElementChangeInfoToDefaults(&ei->change_page[i]);
3386 // start with reading properties for the first change page
3387 xx_current_change_page = 0;
3389 while (!checkEndOfFile(file))
3391 // level file might contain invalid change page number
3392 if (xx_current_change_page >= ei->num_change_pages)
3395 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3397 xx_change = *change; // copy change data into temporary buffer
3399 resetEventBits(); // reset bits; change page might have changed
3401 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3404 *change = xx_change;
3406 setEventFlagsFromEventBits(change);
3408 if (real_chunk_size >= chunk_size)
3412 level->file_has_custom_elements = TRUE;
3414 return real_chunk_size;
3417 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3419 int element = getMappedElement(getFile16BitBE(file));
3420 int real_chunk_size = 2;
3421 struct ElementInfo *ei = &element_info[element];
3422 struct ElementGroupInfo *group = ei->group;
3427 xx_ei = *ei; // copy element data into temporary buffer
3428 xx_group = *group; // copy group data into temporary buffer
3430 while (!checkEndOfFile(file))
3432 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3435 if (real_chunk_size >= chunk_size)
3442 level->file_has_custom_elements = TRUE;
3444 return real_chunk_size;
3447 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3449 int element = getMappedElement(getFile16BitBE(file));
3450 int real_chunk_size = 2;
3451 struct ElementInfo *ei = &element_info[element];
3453 xx_ei = *ei; // copy element data into temporary buffer
3455 while (!checkEndOfFile(file))
3457 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3460 if (real_chunk_size >= chunk_size)
3466 level->file_has_custom_elements = TRUE;
3468 return real_chunk_size;
3471 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3472 struct LevelFileInfo *level_file_info,
3473 boolean level_info_only)
3475 char *filename = level_file_info->filename;
3476 char cookie[MAX_LINE_LEN];
3477 char chunk_name[CHUNK_ID_LEN + 1];
3481 if (!(file = openFile(filename, MODE_READ)))
3483 level->no_valid_file = TRUE;
3484 level->no_level_file = TRUE;
3486 if (level_info_only)
3489 Warn("cannot read level '%s' -- using empty level", filename);
3491 if (!setup.editor.use_template_for_new_levels)
3494 // if level file not found, try to initialize level data from template
3495 filename = getGlobalLevelTemplateFilename();
3497 if (!(file = openFile(filename, MODE_READ)))
3500 // default: for empty levels, use level template for custom elements
3501 level->use_custom_template = TRUE;
3503 level->no_valid_file = FALSE;
3506 getFileChunkBE(file, chunk_name, NULL);
3507 if (strEqual(chunk_name, "RND1"))
3509 getFile32BitBE(file); // not used
3511 getFileChunkBE(file, chunk_name, NULL);
3512 if (!strEqual(chunk_name, "CAVE"))
3514 level->no_valid_file = TRUE;
3516 Warn("unknown format of level file '%s'", filename);
3523 else // check for pre-2.0 file format with cookie string
3525 strcpy(cookie, chunk_name);
3526 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3528 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3529 cookie[strlen(cookie) - 1] = '\0';
3531 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3533 level->no_valid_file = TRUE;
3535 Warn("unknown format of level file '%s'", filename);
3542 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3544 level->no_valid_file = TRUE;
3546 Warn("unsupported version of level file '%s'", filename);
3553 // pre-2.0 level files have no game version, so use file version here
3554 level->game_version = level->file_version;
3557 if (level->file_version < FILE_VERSION_1_2)
3559 // level files from versions before 1.2.0 without chunk structure
3560 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3561 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3569 int (*loader)(File *, int, struct LevelInfo *);
3573 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3574 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3575 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3576 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3577 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3578 { "INFO", -1, LoadLevel_INFO },
3579 { "BODY", -1, LoadLevel_BODY },
3580 { "CONT", -1, LoadLevel_CONT },
3581 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3582 { "CNT3", -1, LoadLevel_CNT3 },
3583 { "CUS1", -1, LoadLevel_CUS1 },
3584 { "CUS2", -1, LoadLevel_CUS2 },
3585 { "CUS3", -1, LoadLevel_CUS3 },
3586 { "CUS4", -1, LoadLevel_CUS4 },
3587 { "GRP1", -1, LoadLevel_GRP1 },
3588 { "CONF", -1, LoadLevel_CONF },
3589 { "ELEM", -1, LoadLevel_ELEM },
3590 { "NOTE", -1, LoadLevel_NOTE },
3591 { "CUSX", -1, LoadLevel_CUSX },
3592 { "GRPX", -1, LoadLevel_GRPX },
3593 { "EMPX", -1, LoadLevel_EMPX },
3598 while (getFileChunkBE(file, chunk_name, &chunk_size))
3602 while (chunk_info[i].name != NULL &&
3603 !strEqual(chunk_name, chunk_info[i].name))
3606 if (chunk_info[i].name == NULL)
3608 Warn("unknown chunk '%s' in level file '%s'",
3609 chunk_name, filename);
3611 ReadUnusedBytesFromFile(file, chunk_size);
3613 else if (chunk_info[i].size != -1 &&
3614 chunk_info[i].size != chunk_size)
3616 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3617 chunk_size, chunk_name, filename);
3619 ReadUnusedBytesFromFile(file, chunk_size);
3623 // call function to load this level chunk
3624 int chunk_size_expected =
3625 (chunk_info[i].loader)(file, chunk_size, level);
3627 if (chunk_size_expected < 0)
3629 Warn("error reading chunk '%s' in level file '%s'",
3630 chunk_name, filename);
3635 // the size of some chunks cannot be checked before reading other
3636 // chunks first (like "HEAD" and "BODY") that contain some header
3637 // information, so check them here
3638 if (chunk_size_expected != chunk_size)
3640 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3641 chunk_size, chunk_name, filename);
3653 // ----------------------------------------------------------------------------
3654 // functions for loading BD level
3655 // ----------------------------------------------------------------------------
3657 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3659 struct LevelInfo_BD *level_bd = level->native_bd_level;
3660 GdCave *cave = NULL; // will be changed below
3661 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3662 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3665 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3667 // cave and map newly allocated when set to defaults above
3668 cave = level_bd->cave;
3670 for (i = 0; i < 5; i++)
3672 cave->level_time[i] = level->time;
3673 cave->level_diamonds[i] = level->gems_needed;
3674 cave->level_magic_wall_time[i] = level->time_magic_wall;
3675 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3678 cave->diamond_value = level->score[SC_DIAMOND];
3679 cave->extra_diamond_value = level->score[SC_DIAMOND];
3681 cave->level_speed[0] = 160; // set cave speed
3683 strncpy(cave->name, level->name, sizeof(GdString));
3684 cave->name[sizeof(GdString) - 1] = '\0';
3686 for (x = 0; x < cave->w; x++)
3687 for (y = 0; y < cave->h; y++)
3688 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3691 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3693 struct LevelInfo_BD *level_bd = level->native_bd_level;
3694 GdCave *cave = level_bd->cave;
3695 int bd_level_nr = level_bd->level_nr;
3698 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3699 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3701 level->time = cave->level_time[bd_level_nr];
3702 level->gems_needed = cave->level_diamonds[bd_level_nr];
3703 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3705 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3706 level->score[SC_DIAMOND] = cave->diamond_value;
3708 strncpy(level->name, cave->name, MAX_LEVEL_NAME_LEN);
3709 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3711 for (x = 0; x < level->fieldx; x++)
3712 for (y = 0; y < level->fieldy; y++)
3713 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3717 // ----------------------------------------------------------------------------
3718 // functions for loading EM level
3719 // ----------------------------------------------------------------------------
3721 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3723 static int ball_xy[8][2] =
3734 struct LevelInfo_EM *level_em = level->native_em_level;
3735 struct CAVE *cav = level_em->cav;
3738 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3739 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3741 cav->time_seconds = level->time;
3742 cav->gems_needed = level->gems_needed;
3744 cav->emerald_score = level->score[SC_EMERALD];
3745 cav->diamond_score = level->score[SC_DIAMOND];
3746 cav->alien_score = level->score[SC_ROBOT];
3747 cav->tank_score = level->score[SC_SPACESHIP];
3748 cav->bug_score = level->score[SC_BUG];
3749 cav->eater_score = level->score[SC_YAMYAM];
3750 cav->nut_score = level->score[SC_NUT];
3751 cav->dynamite_score = level->score[SC_DYNAMITE];
3752 cav->key_score = level->score[SC_KEY];
3753 cav->exit_score = level->score[SC_TIME_BONUS];
3755 cav->num_eater_arrays = level->num_yamyam_contents;
3757 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3758 for (y = 0; y < 3; y++)
3759 for (x = 0; x < 3; x++)
3760 cav->eater_array[i][y * 3 + x] =
3761 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3763 cav->amoeba_time = level->amoeba_speed;
3764 cav->wonderwall_time = level->time_magic_wall;
3765 cav->wheel_time = level->time_wheel;
3767 cav->android_move_time = level->android_move_time;
3768 cav->android_clone_time = level->android_clone_time;
3769 cav->ball_random = level->ball_random;
3770 cav->ball_active = level->ball_active_initial;
3771 cav->ball_time = level->ball_time;
3772 cav->num_ball_arrays = level->num_ball_contents;
3774 cav->lenses_score = level->lenses_score;
3775 cav->magnify_score = level->magnify_score;
3776 cav->slurp_score = level->slurp_score;
3778 cav->lenses_time = level->lenses_time;
3779 cav->magnify_time = level->magnify_time;
3781 cav->wind_time = 9999;
3782 cav->wind_direction =
3783 map_direction_RND_to_EM(level->wind_direction_initial);
3785 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3786 for (j = 0; j < 8; j++)
3787 cav->ball_array[i][j] =
3788 map_element_RND_to_EM_cave(level->ball_content[i].
3789 e[ball_xy[j][0]][ball_xy[j][1]]);
3791 map_android_clone_elements_RND_to_EM(level);
3793 // first fill the complete playfield with the empty space element
3794 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3795 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3796 cav->cave[x][y] = Cblank;
3798 // then copy the real level contents from level file into the playfield
3799 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3801 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3803 if (level->field[x][y] == EL_AMOEBA_DEAD)
3804 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3806 cav->cave[x][y] = new_element;
3809 for (i = 0; i < MAX_PLAYERS; i++)
3811 cav->player_x[i] = -1;
3812 cav->player_y[i] = -1;
3815 // initialize player positions and delete players from the playfield
3816 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3818 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3820 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3822 cav->player_x[player_nr] = x;
3823 cav->player_y[player_nr] = y;
3825 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3830 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3832 static int ball_xy[8][2] =
3843 struct LevelInfo_EM *level_em = level->native_em_level;
3844 struct CAVE *cav = level_em->cav;
3847 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3848 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3850 level->time = cav->time_seconds;
3851 level->gems_needed = cav->gems_needed;
3853 sprintf(level->name, "Level %d", level->file_info.nr);
3855 level->score[SC_EMERALD] = cav->emerald_score;
3856 level->score[SC_DIAMOND] = cav->diamond_score;
3857 level->score[SC_ROBOT] = cav->alien_score;
3858 level->score[SC_SPACESHIP] = cav->tank_score;
3859 level->score[SC_BUG] = cav->bug_score;
3860 level->score[SC_YAMYAM] = cav->eater_score;
3861 level->score[SC_NUT] = cav->nut_score;
3862 level->score[SC_DYNAMITE] = cav->dynamite_score;
3863 level->score[SC_KEY] = cav->key_score;
3864 level->score[SC_TIME_BONUS] = cav->exit_score;
3866 level->num_yamyam_contents = cav->num_eater_arrays;
3868 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3869 for (y = 0; y < 3; y++)
3870 for (x = 0; x < 3; x++)
3871 level->yamyam_content[i].e[x][y] =
3872 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3874 level->amoeba_speed = cav->amoeba_time;
3875 level->time_magic_wall = cav->wonderwall_time;
3876 level->time_wheel = cav->wheel_time;
3878 level->android_move_time = cav->android_move_time;
3879 level->android_clone_time = cav->android_clone_time;
3880 level->ball_random = cav->ball_random;
3881 level->ball_active_initial = cav->ball_active;
3882 level->ball_time = cav->ball_time;
3883 level->num_ball_contents = cav->num_ball_arrays;
3885 level->lenses_score = cav->lenses_score;
3886 level->magnify_score = cav->magnify_score;
3887 level->slurp_score = cav->slurp_score;
3889 level->lenses_time = cav->lenses_time;
3890 level->magnify_time = cav->magnify_time;
3892 level->wind_direction_initial =
3893 map_direction_EM_to_RND(cav->wind_direction);
3895 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3896 for (j = 0; j < 8; j++)
3897 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3898 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3900 map_android_clone_elements_EM_to_RND(level);
3902 // convert the playfield (some elements need special treatment)
3903 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3905 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3907 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3908 new_element = EL_AMOEBA_DEAD;
3910 level->field[x][y] = new_element;
3913 for (i = 0; i < MAX_PLAYERS; i++)
3915 // in case of all players set to the same field, use the first player
3916 int nr = MAX_PLAYERS - i - 1;
3917 int jx = cav->player_x[nr];
3918 int jy = cav->player_y[nr];
3920 if (jx != -1 && jy != -1)
3921 level->field[jx][jy] = EL_PLAYER_1 + nr;
3924 // time score is counted for each 10 seconds left in Emerald Mine levels
3925 level->time_score_base = 10;
3929 // ----------------------------------------------------------------------------
3930 // functions for loading SP level
3931 // ----------------------------------------------------------------------------
3933 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3935 struct LevelInfo_SP *level_sp = level->native_sp_level;
3936 LevelInfoType *header = &level_sp->header;
3939 level_sp->width = level->fieldx;
3940 level_sp->height = level->fieldy;
3942 for (x = 0; x < level->fieldx; x++)
3943 for (y = 0; y < level->fieldy; y++)
3944 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3946 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3948 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3949 header->LevelTitle[i] = level->name[i];
3950 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3952 header->InfotronsNeeded = level->gems_needed;
3954 header->SpecialPortCount = 0;
3956 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3958 boolean gravity_port_found = FALSE;
3959 boolean gravity_port_valid = FALSE;
3960 int gravity_port_flag;
3961 int gravity_port_base_element;
3962 int element = level->field[x][y];
3964 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3965 element <= EL_SP_GRAVITY_ON_PORT_UP)
3967 gravity_port_found = TRUE;
3968 gravity_port_valid = TRUE;
3969 gravity_port_flag = 1;
3970 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3972 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3973 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3975 gravity_port_found = TRUE;
3976 gravity_port_valid = TRUE;
3977 gravity_port_flag = 0;
3978 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3980 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3981 element <= EL_SP_GRAVITY_PORT_UP)
3983 // change R'n'D style gravity inverting special port to normal port
3984 // (there are no gravity inverting ports in native Supaplex engine)
3986 gravity_port_found = TRUE;
3987 gravity_port_valid = FALSE;
3988 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3991 if (gravity_port_found)
3993 if (gravity_port_valid &&
3994 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3996 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3998 port->PortLocation = (y * level->fieldx + x) * 2;
3999 port->Gravity = gravity_port_flag;
4001 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4003 header->SpecialPortCount++;
4007 // change special gravity port to normal port
4009 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4012 level_sp->playfield[x][y] = element - EL_SP_START;
4017 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4019 struct LevelInfo_SP *level_sp = level->native_sp_level;
4020 LevelInfoType *header = &level_sp->header;
4021 boolean num_invalid_elements = 0;
4024 level->fieldx = level_sp->width;
4025 level->fieldy = level_sp->height;
4027 for (x = 0; x < level->fieldx; x++)
4029 for (y = 0; y < level->fieldy; y++)
4031 int element_old = level_sp->playfield[x][y];
4032 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4034 if (element_new == EL_UNKNOWN)
4036 num_invalid_elements++;
4038 Debug("level:native:SP", "invalid element %d at position %d, %d",
4042 level->field[x][y] = element_new;
4046 if (num_invalid_elements > 0)
4047 Warn("found %d invalid elements%s", num_invalid_elements,
4048 (!options.debug ? " (use '--debug' for more details)" : ""));
4050 for (i = 0; i < MAX_PLAYERS; i++)
4051 level->initial_player_gravity[i] =
4052 (header->InitialGravity == 1 ? TRUE : FALSE);
4054 // skip leading spaces
4055 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4056 if (header->LevelTitle[i] != ' ')
4060 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4061 level->name[j] = header->LevelTitle[i];
4062 level->name[j] = '\0';
4064 // cut trailing spaces
4066 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4067 level->name[j - 1] = '\0';
4069 level->gems_needed = header->InfotronsNeeded;
4071 for (i = 0; i < header->SpecialPortCount; i++)
4073 SpecialPortType *port = &header->SpecialPort[i];
4074 int port_location = port->PortLocation;
4075 int gravity = port->Gravity;
4076 int port_x, port_y, port_element;
4078 port_x = (port_location / 2) % level->fieldx;
4079 port_y = (port_location / 2) / level->fieldx;
4081 if (port_x < 0 || port_x >= level->fieldx ||
4082 port_y < 0 || port_y >= level->fieldy)
4084 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4089 port_element = level->field[port_x][port_y];
4091 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4092 port_element > EL_SP_GRAVITY_PORT_UP)
4094 Warn("no special port at position (%d, %d)", port_x, port_y);
4099 // change previous (wrong) gravity inverting special port to either
4100 // gravity enabling special port or gravity disabling special port
4101 level->field[port_x][port_y] +=
4102 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4103 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4106 // change special gravity ports without database entries to normal ports
4107 for (x = 0; x < level->fieldx; x++)
4108 for (y = 0; y < level->fieldy; y++)
4109 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4110 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4111 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4113 level->time = 0; // no time limit
4114 level->amoeba_speed = 0;
4115 level->time_magic_wall = 0;
4116 level->time_wheel = 0;
4117 level->amoeba_content = EL_EMPTY;
4119 // original Supaplex does not use score values -- rate by playing time
4120 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4121 level->score[i] = 0;
4123 level->rate_time_over_score = TRUE;
4125 // there are no yamyams in supaplex levels
4126 for (i = 0; i < level->num_yamyam_contents; i++)
4127 for (x = 0; x < 3; x++)
4128 for (y = 0; y < 3; y++)
4129 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4132 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4134 struct LevelInfo_SP *level_sp = level->native_sp_level;
4135 struct DemoInfo_SP *demo = &level_sp->demo;
4138 // always start with reliable default values
4139 demo->is_available = FALSE;
4142 if (TAPE_IS_EMPTY(tape))
4145 demo->level_nr = tape.level_nr; // (currently not used)
4147 level_sp->header.DemoRandomSeed = tape.random_seed;
4151 for (i = 0; i < tape.length; i++)
4153 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4154 int demo_repeat = tape.pos[i].delay;
4155 int demo_entries = (demo_repeat + 15) / 16;
4157 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4159 Warn("tape truncated: size exceeds maximum SP demo size %d",
4165 for (j = 0; j < demo_repeat / 16; j++)
4166 demo->data[demo->length++] = 0xf0 | demo_action;
4168 if (demo_repeat % 16)
4169 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4172 demo->is_available = TRUE;
4175 static void setTapeInfoToDefaults(void);
4177 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4179 struct LevelInfo_SP *level_sp = level->native_sp_level;
4180 struct DemoInfo_SP *demo = &level_sp->demo;
4181 char *filename = level->file_info.filename;
4184 // always start with reliable default values
4185 setTapeInfoToDefaults();
4187 if (!demo->is_available)
4190 tape.level_nr = demo->level_nr; // (currently not used)
4191 tape.random_seed = level_sp->header.DemoRandomSeed;
4193 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4196 tape.pos[tape.counter].delay = 0;
4198 for (i = 0; i < demo->length; i++)
4200 int demo_action = demo->data[i] & 0x0f;
4201 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4202 int tape_action = map_key_SP_to_RND(demo_action);
4203 int tape_repeat = demo_repeat + 1;
4204 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4205 boolean success = 0;
4208 for (j = 0; j < tape_repeat; j++)
4209 success = TapeAddAction(action);
4213 Warn("SP demo truncated: size exceeds maximum tape size %d",
4220 TapeHaltRecording();
4224 // ----------------------------------------------------------------------------
4225 // functions for loading MM level
4226 // ----------------------------------------------------------------------------
4228 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4230 struct LevelInfo_MM *level_mm = level->native_mm_level;
4233 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4234 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4236 level_mm->time = level->time;
4237 level_mm->kettles_needed = level->gems_needed;
4238 level_mm->auto_count_kettles = level->auto_count_gems;
4240 level_mm->mm_laser_red = level->mm_laser_red;
4241 level_mm->mm_laser_green = level->mm_laser_green;
4242 level_mm->mm_laser_blue = level->mm_laser_blue;
4244 level_mm->df_laser_red = level->df_laser_red;
4245 level_mm->df_laser_green = level->df_laser_green;
4246 level_mm->df_laser_blue = level->df_laser_blue;
4248 strcpy(level_mm->name, level->name);
4249 strcpy(level_mm->author, level->author);
4251 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4252 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4253 level_mm->score[SC_KEY] = level->score[SC_KEY];
4254 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4255 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4257 level_mm->amoeba_speed = level->amoeba_speed;
4258 level_mm->time_fuse = level->mm_time_fuse;
4259 level_mm->time_bomb = level->mm_time_bomb;
4260 level_mm->time_ball = level->mm_time_ball;
4261 level_mm->time_block = level->mm_time_block;
4263 level_mm->num_ball_contents = level->num_mm_ball_contents;
4264 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4265 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4266 level_mm->explode_ball = level->explode_mm_ball;
4268 for (i = 0; i < level->num_mm_ball_contents; i++)
4269 level_mm->ball_content[i] =
4270 map_element_RND_to_MM(level->mm_ball_content[i]);
4272 for (x = 0; x < level->fieldx; x++)
4273 for (y = 0; y < level->fieldy; y++)
4275 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4278 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4280 struct LevelInfo_MM *level_mm = level->native_mm_level;
4283 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4284 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4286 level->time = level_mm->time;
4287 level->gems_needed = level_mm->kettles_needed;
4288 level->auto_count_gems = level_mm->auto_count_kettles;
4290 level->mm_laser_red = level_mm->mm_laser_red;
4291 level->mm_laser_green = level_mm->mm_laser_green;
4292 level->mm_laser_blue = level_mm->mm_laser_blue;
4294 level->df_laser_red = level_mm->df_laser_red;
4295 level->df_laser_green = level_mm->df_laser_green;
4296 level->df_laser_blue = level_mm->df_laser_blue;
4298 strcpy(level->name, level_mm->name);
4300 // only overwrite author from 'levelinfo.conf' if author defined in level
4301 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4302 strcpy(level->author, level_mm->author);
4304 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4305 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4306 level->score[SC_KEY] = level_mm->score[SC_KEY];
4307 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4308 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4310 level->amoeba_speed = level_mm->amoeba_speed;
4311 level->mm_time_fuse = level_mm->time_fuse;
4312 level->mm_time_bomb = level_mm->time_bomb;
4313 level->mm_time_ball = level_mm->time_ball;
4314 level->mm_time_block = level_mm->time_block;
4316 level->num_mm_ball_contents = level_mm->num_ball_contents;
4317 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4318 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4319 level->explode_mm_ball = level_mm->explode_ball;
4321 for (i = 0; i < level->num_mm_ball_contents; i++)
4322 level->mm_ball_content[i] =
4323 map_element_MM_to_RND(level_mm->ball_content[i]);
4325 for (x = 0; x < level->fieldx; x++)
4326 for (y = 0; y < level->fieldy; y++)
4327 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4331 // ----------------------------------------------------------------------------
4332 // functions for loading DC level
4333 // ----------------------------------------------------------------------------
4335 #define DC_LEVEL_HEADER_SIZE 344
4337 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4340 static int last_data_encoded;
4344 int diff_hi, diff_lo;
4345 int data_hi, data_lo;
4346 unsigned short data_decoded;
4350 last_data_encoded = 0;
4357 diff = data_encoded - last_data_encoded;
4358 diff_hi = diff & ~0xff;
4359 diff_lo = diff & 0xff;
4363 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4364 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4365 data_hi = data_hi & 0xff00;
4367 data_decoded = data_hi | data_lo;
4369 last_data_encoded = data_encoded;
4371 offset1 = (offset1 + 1) % 31;
4372 offset2 = offset2 & 0xff;
4374 return data_decoded;
4377 static int getMappedElement_DC(int element)
4385 // 0x0117 - 0x036e: (?)
4388 // 0x042d - 0x0684: (?)
4404 element = EL_CRYSTAL;
4407 case 0x0e77: // quicksand (boulder)
4408 element = EL_QUICKSAND_FAST_FULL;
4411 case 0x0e99: // slow quicksand (boulder)
4412 element = EL_QUICKSAND_FULL;
4416 element = EL_EM_EXIT_OPEN;
4420 element = EL_EM_EXIT_CLOSED;
4424 element = EL_EM_STEEL_EXIT_OPEN;
4428 element = EL_EM_STEEL_EXIT_CLOSED;
4431 case 0x0f4f: // dynamite (lit 1)
4432 element = EL_EM_DYNAMITE_ACTIVE;
4435 case 0x0f57: // dynamite (lit 2)
4436 element = EL_EM_DYNAMITE_ACTIVE;
4439 case 0x0f5f: // dynamite (lit 3)
4440 element = EL_EM_DYNAMITE_ACTIVE;
4443 case 0x0f67: // dynamite (lit 4)
4444 element = EL_EM_DYNAMITE_ACTIVE;
4451 element = EL_AMOEBA_WET;
4455 element = EL_AMOEBA_DROP;
4459 element = EL_DC_MAGIC_WALL;
4463 element = EL_SPACESHIP_UP;
4467 element = EL_SPACESHIP_DOWN;
4471 element = EL_SPACESHIP_LEFT;
4475 element = EL_SPACESHIP_RIGHT;
4479 element = EL_BUG_UP;
4483 element = EL_BUG_DOWN;
4487 element = EL_BUG_LEFT;
4491 element = EL_BUG_RIGHT;
4495 element = EL_MOLE_UP;
4499 element = EL_MOLE_DOWN;
4503 element = EL_MOLE_LEFT;
4507 element = EL_MOLE_RIGHT;
4515 element = EL_YAMYAM_UP;
4519 element = EL_SWITCHGATE_OPEN;
4523 element = EL_SWITCHGATE_CLOSED;
4527 element = EL_DC_SWITCHGATE_SWITCH_UP;
4531 element = EL_TIMEGATE_CLOSED;
4534 case 0x144c: // conveyor belt switch (green)
4535 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4538 case 0x144f: // conveyor belt switch (red)
4539 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4542 case 0x1452: // conveyor belt switch (blue)
4543 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4547 element = EL_CONVEYOR_BELT_3_MIDDLE;
4551 element = EL_CONVEYOR_BELT_3_LEFT;
4555 element = EL_CONVEYOR_BELT_3_RIGHT;
4559 element = EL_CONVEYOR_BELT_1_MIDDLE;
4563 element = EL_CONVEYOR_BELT_1_LEFT;
4567 element = EL_CONVEYOR_BELT_1_RIGHT;
4571 element = EL_CONVEYOR_BELT_4_MIDDLE;
4575 element = EL_CONVEYOR_BELT_4_LEFT;
4579 element = EL_CONVEYOR_BELT_4_RIGHT;
4583 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4587 element = EL_EXPANDABLE_WALL_VERTICAL;
4591 element = EL_EXPANDABLE_WALL_ANY;
4594 case 0x14ce: // growing steel wall (left/right)
4595 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4598 case 0x14df: // growing steel wall (up/down)
4599 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4602 case 0x14e8: // growing steel wall (up/down/left/right)
4603 element = EL_EXPANDABLE_STEELWALL_ANY;
4607 element = EL_SHIELD_DEADLY;
4611 element = EL_EXTRA_TIME;
4619 element = EL_EMPTY_SPACE;
4622 case 0x1578: // quicksand (empty)
4623 element = EL_QUICKSAND_FAST_EMPTY;
4626 case 0x1579: // slow quicksand (empty)
4627 element = EL_QUICKSAND_EMPTY;
4637 element = EL_EM_DYNAMITE;
4640 case 0x15a1: // key (red)
4641 element = EL_EM_KEY_1;
4644 case 0x15a2: // key (yellow)
4645 element = EL_EM_KEY_2;
4648 case 0x15a3: // key (blue)
4649 element = EL_EM_KEY_4;
4652 case 0x15a4: // key (green)
4653 element = EL_EM_KEY_3;
4656 case 0x15a5: // key (white)
4657 element = EL_DC_KEY_WHITE;
4661 element = EL_WALL_SLIPPERY;
4668 case 0x15a8: // wall (not round)
4672 case 0x15a9: // (blue)
4673 element = EL_CHAR_A;
4676 case 0x15aa: // (blue)
4677 element = EL_CHAR_B;
4680 case 0x15ab: // (blue)
4681 element = EL_CHAR_C;
4684 case 0x15ac: // (blue)
4685 element = EL_CHAR_D;
4688 case 0x15ad: // (blue)
4689 element = EL_CHAR_E;
4692 case 0x15ae: // (blue)
4693 element = EL_CHAR_F;
4696 case 0x15af: // (blue)
4697 element = EL_CHAR_G;
4700 case 0x15b0: // (blue)
4701 element = EL_CHAR_H;
4704 case 0x15b1: // (blue)
4705 element = EL_CHAR_I;
4708 case 0x15b2: // (blue)
4709 element = EL_CHAR_J;
4712 case 0x15b3: // (blue)
4713 element = EL_CHAR_K;
4716 case 0x15b4: // (blue)
4717 element = EL_CHAR_L;
4720 case 0x15b5: // (blue)
4721 element = EL_CHAR_M;
4724 case 0x15b6: // (blue)
4725 element = EL_CHAR_N;
4728 case 0x15b7: // (blue)
4729 element = EL_CHAR_O;
4732 case 0x15b8: // (blue)
4733 element = EL_CHAR_P;
4736 case 0x15b9: // (blue)
4737 element = EL_CHAR_Q;
4740 case 0x15ba: // (blue)
4741 element = EL_CHAR_R;
4744 case 0x15bb: // (blue)
4745 element = EL_CHAR_S;
4748 case 0x15bc: // (blue)
4749 element = EL_CHAR_T;
4752 case 0x15bd: // (blue)
4753 element = EL_CHAR_U;
4756 case 0x15be: // (blue)
4757 element = EL_CHAR_V;
4760 case 0x15bf: // (blue)
4761 element = EL_CHAR_W;
4764 case 0x15c0: // (blue)
4765 element = EL_CHAR_X;
4768 case 0x15c1: // (blue)
4769 element = EL_CHAR_Y;
4772 case 0x15c2: // (blue)
4773 element = EL_CHAR_Z;
4776 case 0x15c3: // (blue)
4777 element = EL_CHAR_AUMLAUT;
4780 case 0x15c4: // (blue)
4781 element = EL_CHAR_OUMLAUT;
4784 case 0x15c5: // (blue)
4785 element = EL_CHAR_UUMLAUT;
4788 case 0x15c6: // (blue)
4789 element = EL_CHAR_0;
4792 case 0x15c7: // (blue)
4793 element = EL_CHAR_1;
4796 case 0x15c8: // (blue)
4797 element = EL_CHAR_2;
4800 case 0x15c9: // (blue)
4801 element = EL_CHAR_3;
4804 case 0x15ca: // (blue)
4805 element = EL_CHAR_4;
4808 case 0x15cb: // (blue)
4809 element = EL_CHAR_5;
4812 case 0x15cc: // (blue)
4813 element = EL_CHAR_6;
4816 case 0x15cd: // (blue)
4817 element = EL_CHAR_7;
4820 case 0x15ce: // (blue)
4821 element = EL_CHAR_8;
4824 case 0x15cf: // (blue)
4825 element = EL_CHAR_9;
4828 case 0x15d0: // (blue)
4829 element = EL_CHAR_PERIOD;
4832 case 0x15d1: // (blue)
4833 element = EL_CHAR_EXCLAM;
4836 case 0x15d2: // (blue)
4837 element = EL_CHAR_COLON;
4840 case 0x15d3: // (blue)
4841 element = EL_CHAR_LESS;
4844 case 0x15d4: // (blue)
4845 element = EL_CHAR_GREATER;
4848 case 0x15d5: // (blue)
4849 element = EL_CHAR_QUESTION;
4852 case 0x15d6: // (blue)
4853 element = EL_CHAR_COPYRIGHT;
4856 case 0x15d7: // (blue)
4857 element = EL_CHAR_UP;
4860 case 0x15d8: // (blue)
4861 element = EL_CHAR_DOWN;
4864 case 0x15d9: // (blue)
4865 element = EL_CHAR_BUTTON;
4868 case 0x15da: // (blue)
4869 element = EL_CHAR_PLUS;
4872 case 0x15db: // (blue)
4873 element = EL_CHAR_MINUS;
4876 case 0x15dc: // (blue)
4877 element = EL_CHAR_APOSTROPHE;
4880 case 0x15dd: // (blue)
4881 element = EL_CHAR_PARENLEFT;
4884 case 0x15de: // (blue)
4885 element = EL_CHAR_PARENRIGHT;
4888 case 0x15df: // (green)
4889 element = EL_CHAR_A;
4892 case 0x15e0: // (green)
4893 element = EL_CHAR_B;
4896 case 0x15e1: // (green)
4897 element = EL_CHAR_C;
4900 case 0x15e2: // (green)
4901 element = EL_CHAR_D;
4904 case 0x15e3: // (green)
4905 element = EL_CHAR_E;
4908 case 0x15e4: // (green)
4909 element = EL_CHAR_F;
4912 case 0x15e5: // (green)
4913 element = EL_CHAR_G;
4916 case 0x15e6: // (green)
4917 element = EL_CHAR_H;
4920 case 0x15e7: // (green)
4921 element = EL_CHAR_I;
4924 case 0x15e8: // (green)
4925 element = EL_CHAR_J;
4928 case 0x15e9: // (green)
4929 element = EL_CHAR_K;
4932 case 0x15ea: // (green)
4933 element = EL_CHAR_L;
4936 case 0x15eb: // (green)
4937 element = EL_CHAR_M;
4940 case 0x15ec: // (green)
4941 element = EL_CHAR_N;
4944 case 0x15ed: // (green)
4945 element = EL_CHAR_O;
4948 case 0x15ee: // (green)
4949 element = EL_CHAR_P;
4952 case 0x15ef: // (green)
4953 element = EL_CHAR_Q;
4956 case 0x15f0: // (green)
4957 element = EL_CHAR_R;
4960 case 0x15f1: // (green)
4961 element = EL_CHAR_S;
4964 case 0x15f2: // (green)
4965 element = EL_CHAR_T;
4968 case 0x15f3: // (green)
4969 element = EL_CHAR_U;
4972 case 0x15f4: // (green)
4973 element = EL_CHAR_V;
4976 case 0x15f5: // (green)
4977 element = EL_CHAR_W;
4980 case 0x15f6: // (green)
4981 element = EL_CHAR_X;
4984 case 0x15f7: // (green)
4985 element = EL_CHAR_Y;
4988 case 0x15f8: // (green)
4989 element = EL_CHAR_Z;
4992 case 0x15f9: // (green)
4993 element = EL_CHAR_AUMLAUT;
4996 case 0x15fa: // (green)
4997 element = EL_CHAR_OUMLAUT;
5000 case 0x15fb: // (green)
5001 element = EL_CHAR_UUMLAUT;
5004 case 0x15fc: // (green)
5005 element = EL_CHAR_0;
5008 case 0x15fd: // (green)
5009 element = EL_CHAR_1;
5012 case 0x15fe: // (green)
5013 element = EL_CHAR_2;
5016 case 0x15ff: // (green)
5017 element = EL_CHAR_3;
5020 case 0x1600: // (green)
5021 element = EL_CHAR_4;
5024 case 0x1601: // (green)
5025 element = EL_CHAR_5;
5028 case 0x1602: // (green)
5029 element = EL_CHAR_6;
5032 case 0x1603: // (green)
5033 element = EL_CHAR_7;
5036 case 0x1604: // (green)
5037 element = EL_CHAR_8;
5040 case 0x1605: // (green)
5041 element = EL_CHAR_9;
5044 case 0x1606: // (green)
5045 element = EL_CHAR_PERIOD;
5048 case 0x1607: // (green)
5049 element = EL_CHAR_EXCLAM;
5052 case 0x1608: // (green)
5053 element = EL_CHAR_COLON;
5056 case 0x1609: // (green)
5057 element = EL_CHAR_LESS;
5060 case 0x160a: // (green)
5061 element = EL_CHAR_GREATER;
5064 case 0x160b: // (green)
5065 element = EL_CHAR_QUESTION;
5068 case 0x160c: // (green)
5069 element = EL_CHAR_COPYRIGHT;
5072 case 0x160d: // (green)
5073 element = EL_CHAR_UP;
5076 case 0x160e: // (green)
5077 element = EL_CHAR_DOWN;
5080 case 0x160f: // (green)
5081 element = EL_CHAR_BUTTON;
5084 case 0x1610: // (green)
5085 element = EL_CHAR_PLUS;
5088 case 0x1611: // (green)
5089 element = EL_CHAR_MINUS;
5092 case 0x1612: // (green)
5093 element = EL_CHAR_APOSTROPHE;
5096 case 0x1613: // (green)
5097 element = EL_CHAR_PARENLEFT;
5100 case 0x1614: // (green)
5101 element = EL_CHAR_PARENRIGHT;
5104 case 0x1615: // (blue steel)
5105 element = EL_STEEL_CHAR_A;
5108 case 0x1616: // (blue steel)
5109 element = EL_STEEL_CHAR_B;
5112 case 0x1617: // (blue steel)
5113 element = EL_STEEL_CHAR_C;
5116 case 0x1618: // (blue steel)
5117 element = EL_STEEL_CHAR_D;
5120 case 0x1619: // (blue steel)
5121 element = EL_STEEL_CHAR_E;
5124 case 0x161a: // (blue steel)
5125 element = EL_STEEL_CHAR_F;
5128 case 0x161b: // (blue steel)
5129 element = EL_STEEL_CHAR_G;
5132 case 0x161c: // (blue steel)
5133 element = EL_STEEL_CHAR_H;
5136 case 0x161d: // (blue steel)
5137 element = EL_STEEL_CHAR_I;
5140 case 0x161e: // (blue steel)
5141 element = EL_STEEL_CHAR_J;
5144 case 0x161f: // (blue steel)
5145 element = EL_STEEL_CHAR_K;
5148 case 0x1620: // (blue steel)
5149 element = EL_STEEL_CHAR_L;
5152 case 0x1621: // (blue steel)
5153 element = EL_STEEL_CHAR_M;
5156 case 0x1622: // (blue steel)
5157 element = EL_STEEL_CHAR_N;
5160 case 0x1623: // (blue steel)
5161 element = EL_STEEL_CHAR_O;
5164 case 0x1624: // (blue steel)
5165 element = EL_STEEL_CHAR_P;
5168 case 0x1625: // (blue steel)
5169 element = EL_STEEL_CHAR_Q;
5172 case 0x1626: // (blue steel)
5173 element = EL_STEEL_CHAR_R;
5176 case 0x1627: // (blue steel)
5177 element = EL_STEEL_CHAR_S;
5180 case 0x1628: // (blue steel)
5181 element = EL_STEEL_CHAR_T;
5184 case 0x1629: // (blue steel)
5185 element = EL_STEEL_CHAR_U;
5188 case 0x162a: // (blue steel)
5189 element = EL_STEEL_CHAR_V;
5192 case 0x162b: // (blue steel)
5193 element = EL_STEEL_CHAR_W;
5196 case 0x162c: // (blue steel)
5197 element = EL_STEEL_CHAR_X;
5200 case 0x162d: // (blue steel)
5201 element = EL_STEEL_CHAR_Y;
5204 case 0x162e: // (blue steel)
5205 element = EL_STEEL_CHAR_Z;
5208 case 0x162f: // (blue steel)
5209 element = EL_STEEL_CHAR_AUMLAUT;
5212 case 0x1630: // (blue steel)
5213 element = EL_STEEL_CHAR_OUMLAUT;
5216 case 0x1631: // (blue steel)
5217 element = EL_STEEL_CHAR_UUMLAUT;
5220 case 0x1632: // (blue steel)
5221 element = EL_STEEL_CHAR_0;
5224 case 0x1633: // (blue steel)
5225 element = EL_STEEL_CHAR_1;
5228 case 0x1634: // (blue steel)
5229 element = EL_STEEL_CHAR_2;
5232 case 0x1635: // (blue steel)
5233 element = EL_STEEL_CHAR_3;
5236 case 0x1636: // (blue steel)
5237 element = EL_STEEL_CHAR_4;
5240 case 0x1637: // (blue steel)
5241 element = EL_STEEL_CHAR_5;
5244 case 0x1638: // (blue steel)
5245 element = EL_STEEL_CHAR_6;
5248 case 0x1639: // (blue steel)
5249 element = EL_STEEL_CHAR_7;
5252 case 0x163a: // (blue steel)
5253 element = EL_STEEL_CHAR_8;
5256 case 0x163b: // (blue steel)
5257 element = EL_STEEL_CHAR_9;
5260 case 0x163c: // (blue steel)
5261 element = EL_STEEL_CHAR_PERIOD;
5264 case 0x163d: // (blue steel)
5265 element = EL_STEEL_CHAR_EXCLAM;
5268 case 0x163e: // (blue steel)
5269 element = EL_STEEL_CHAR_COLON;
5272 case 0x163f: // (blue steel)
5273 element = EL_STEEL_CHAR_LESS;
5276 case 0x1640: // (blue steel)
5277 element = EL_STEEL_CHAR_GREATER;
5280 case 0x1641: // (blue steel)
5281 element = EL_STEEL_CHAR_QUESTION;
5284 case 0x1642: // (blue steel)
5285 element = EL_STEEL_CHAR_COPYRIGHT;
5288 case 0x1643: // (blue steel)
5289 element = EL_STEEL_CHAR_UP;
5292 case 0x1644: // (blue steel)
5293 element = EL_STEEL_CHAR_DOWN;
5296 case 0x1645: // (blue steel)
5297 element = EL_STEEL_CHAR_BUTTON;
5300 case 0x1646: // (blue steel)
5301 element = EL_STEEL_CHAR_PLUS;
5304 case 0x1647: // (blue steel)
5305 element = EL_STEEL_CHAR_MINUS;
5308 case 0x1648: // (blue steel)
5309 element = EL_STEEL_CHAR_APOSTROPHE;
5312 case 0x1649: // (blue steel)
5313 element = EL_STEEL_CHAR_PARENLEFT;
5316 case 0x164a: // (blue steel)
5317 element = EL_STEEL_CHAR_PARENRIGHT;
5320 case 0x164b: // (green steel)
5321 element = EL_STEEL_CHAR_A;
5324 case 0x164c: // (green steel)
5325 element = EL_STEEL_CHAR_B;
5328 case 0x164d: // (green steel)
5329 element = EL_STEEL_CHAR_C;
5332 case 0x164e: // (green steel)
5333 element = EL_STEEL_CHAR_D;
5336 case 0x164f: // (green steel)
5337 element = EL_STEEL_CHAR_E;
5340 case 0x1650: // (green steel)
5341 element = EL_STEEL_CHAR_F;
5344 case 0x1651: // (green steel)
5345 element = EL_STEEL_CHAR_G;
5348 case 0x1652: // (green steel)
5349 element = EL_STEEL_CHAR_H;
5352 case 0x1653: // (green steel)
5353 element = EL_STEEL_CHAR_I;
5356 case 0x1654: // (green steel)
5357 element = EL_STEEL_CHAR_J;
5360 case 0x1655: // (green steel)
5361 element = EL_STEEL_CHAR_K;
5364 case 0x1656: // (green steel)
5365 element = EL_STEEL_CHAR_L;
5368 case 0x1657: // (green steel)
5369 element = EL_STEEL_CHAR_M;
5372 case 0x1658: // (green steel)
5373 element = EL_STEEL_CHAR_N;
5376 case 0x1659: // (green steel)
5377 element = EL_STEEL_CHAR_O;
5380 case 0x165a: // (green steel)
5381 element = EL_STEEL_CHAR_P;
5384 case 0x165b: // (green steel)
5385 element = EL_STEEL_CHAR_Q;
5388 case 0x165c: // (green steel)
5389 element = EL_STEEL_CHAR_R;
5392 case 0x165d: // (green steel)
5393 element = EL_STEEL_CHAR_S;
5396 case 0x165e: // (green steel)
5397 element = EL_STEEL_CHAR_T;
5400 case 0x165f: // (green steel)
5401 element = EL_STEEL_CHAR_U;
5404 case 0x1660: // (green steel)
5405 element = EL_STEEL_CHAR_V;
5408 case 0x1661: // (green steel)
5409 element = EL_STEEL_CHAR_W;
5412 case 0x1662: // (green steel)
5413 element = EL_STEEL_CHAR_X;
5416 case 0x1663: // (green steel)
5417 element = EL_STEEL_CHAR_Y;
5420 case 0x1664: // (green steel)
5421 element = EL_STEEL_CHAR_Z;
5424 case 0x1665: // (green steel)
5425 element = EL_STEEL_CHAR_AUMLAUT;
5428 case 0x1666: // (green steel)
5429 element = EL_STEEL_CHAR_OUMLAUT;
5432 case 0x1667: // (green steel)
5433 element = EL_STEEL_CHAR_UUMLAUT;
5436 case 0x1668: // (green steel)
5437 element = EL_STEEL_CHAR_0;
5440 case 0x1669: // (green steel)
5441 element = EL_STEEL_CHAR_1;
5444 case 0x166a: // (green steel)
5445 element = EL_STEEL_CHAR_2;
5448 case 0x166b: // (green steel)
5449 element = EL_STEEL_CHAR_3;
5452 case 0x166c: // (green steel)
5453 element = EL_STEEL_CHAR_4;
5456 case 0x166d: // (green steel)
5457 element = EL_STEEL_CHAR_5;
5460 case 0x166e: // (green steel)
5461 element = EL_STEEL_CHAR_6;
5464 case 0x166f: // (green steel)
5465 element = EL_STEEL_CHAR_7;
5468 case 0x1670: // (green steel)
5469 element = EL_STEEL_CHAR_8;
5472 case 0x1671: // (green steel)
5473 element = EL_STEEL_CHAR_9;
5476 case 0x1672: // (green steel)
5477 element = EL_STEEL_CHAR_PERIOD;
5480 case 0x1673: // (green steel)
5481 element = EL_STEEL_CHAR_EXCLAM;
5484 case 0x1674: // (green steel)
5485 element = EL_STEEL_CHAR_COLON;
5488 case 0x1675: // (green steel)
5489 element = EL_STEEL_CHAR_LESS;
5492 case 0x1676: // (green steel)
5493 element = EL_STEEL_CHAR_GREATER;
5496 case 0x1677: // (green steel)
5497 element = EL_STEEL_CHAR_QUESTION;
5500 case 0x1678: // (green steel)
5501 element = EL_STEEL_CHAR_COPYRIGHT;
5504 case 0x1679: // (green steel)
5505 element = EL_STEEL_CHAR_UP;
5508 case 0x167a: // (green steel)
5509 element = EL_STEEL_CHAR_DOWN;
5512 case 0x167b: // (green steel)
5513 element = EL_STEEL_CHAR_BUTTON;
5516 case 0x167c: // (green steel)
5517 element = EL_STEEL_CHAR_PLUS;
5520 case 0x167d: // (green steel)
5521 element = EL_STEEL_CHAR_MINUS;
5524 case 0x167e: // (green steel)
5525 element = EL_STEEL_CHAR_APOSTROPHE;
5528 case 0x167f: // (green steel)
5529 element = EL_STEEL_CHAR_PARENLEFT;
5532 case 0x1680: // (green steel)
5533 element = EL_STEEL_CHAR_PARENRIGHT;
5536 case 0x1681: // gate (red)
5537 element = EL_EM_GATE_1;
5540 case 0x1682: // secret gate (red)
5541 element = EL_EM_GATE_1_GRAY;
5544 case 0x1683: // gate (yellow)
5545 element = EL_EM_GATE_2;
5548 case 0x1684: // secret gate (yellow)
5549 element = EL_EM_GATE_2_GRAY;
5552 case 0x1685: // gate (blue)
5553 element = EL_EM_GATE_4;
5556 case 0x1686: // secret gate (blue)
5557 element = EL_EM_GATE_4_GRAY;
5560 case 0x1687: // gate (green)
5561 element = EL_EM_GATE_3;
5564 case 0x1688: // secret gate (green)
5565 element = EL_EM_GATE_3_GRAY;
5568 case 0x1689: // gate (white)
5569 element = EL_DC_GATE_WHITE;
5572 case 0x168a: // secret gate (white)
5573 element = EL_DC_GATE_WHITE_GRAY;
5576 case 0x168b: // secret gate (no key)
5577 element = EL_DC_GATE_FAKE_GRAY;
5581 element = EL_ROBOT_WHEEL;
5585 element = EL_DC_TIMEGATE_SWITCH;
5589 element = EL_ACID_POOL_BOTTOM;
5593 element = EL_ACID_POOL_TOPLEFT;
5597 element = EL_ACID_POOL_TOPRIGHT;
5601 element = EL_ACID_POOL_BOTTOMLEFT;
5605 element = EL_ACID_POOL_BOTTOMRIGHT;
5609 element = EL_STEELWALL;
5613 element = EL_STEELWALL_SLIPPERY;
5616 case 0x1695: // steel wall (not round)
5617 element = EL_STEELWALL;
5620 case 0x1696: // steel wall (left)
5621 element = EL_DC_STEELWALL_1_LEFT;
5624 case 0x1697: // steel wall (bottom)
5625 element = EL_DC_STEELWALL_1_BOTTOM;
5628 case 0x1698: // steel wall (right)
5629 element = EL_DC_STEELWALL_1_RIGHT;
5632 case 0x1699: // steel wall (top)
5633 element = EL_DC_STEELWALL_1_TOP;
5636 case 0x169a: // steel wall (left/bottom)
5637 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5640 case 0x169b: // steel wall (right/bottom)
5641 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5644 case 0x169c: // steel wall (right/top)
5645 element = EL_DC_STEELWALL_1_TOPRIGHT;
5648 case 0x169d: // steel wall (left/top)
5649 element = EL_DC_STEELWALL_1_TOPLEFT;
5652 case 0x169e: // steel wall (right/bottom small)
5653 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5656 case 0x169f: // steel wall (left/bottom small)
5657 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5660 case 0x16a0: // steel wall (right/top small)
5661 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5664 case 0x16a1: // steel wall (left/top small)
5665 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5668 case 0x16a2: // steel wall (left/right)
5669 element = EL_DC_STEELWALL_1_VERTICAL;
5672 case 0x16a3: // steel wall (top/bottom)
5673 element = EL_DC_STEELWALL_1_HORIZONTAL;
5676 case 0x16a4: // steel wall 2 (left end)
5677 element = EL_DC_STEELWALL_2_LEFT;
5680 case 0x16a5: // steel wall 2 (right end)
5681 element = EL_DC_STEELWALL_2_RIGHT;
5684 case 0x16a6: // steel wall 2 (top end)
5685 element = EL_DC_STEELWALL_2_TOP;
5688 case 0x16a7: // steel wall 2 (bottom end)
5689 element = EL_DC_STEELWALL_2_BOTTOM;
5692 case 0x16a8: // steel wall 2 (left/right)
5693 element = EL_DC_STEELWALL_2_HORIZONTAL;
5696 case 0x16a9: // steel wall 2 (up/down)
5697 element = EL_DC_STEELWALL_2_VERTICAL;
5700 case 0x16aa: // steel wall 2 (mid)
5701 element = EL_DC_STEELWALL_2_MIDDLE;
5705 element = EL_SIGN_EXCLAMATION;
5709 element = EL_SIGN_RADIOACTIVITY;
5713 element = EL_SIGN_STOP;
5717 element = EL_SIGN_WHEELCHAIR;
5721 element = EL_SIGN_PARKING;
5725 element = EL_SIGN_NO_ENTRY;
5729 element = EL_SIGN_HEART;
5733 element = EL_SIGN_GIVE_WAY;
5737 element = EL_SIGN_ENTRY_FORBIDDEN;
5741 element = EL_SIGN_EMERGENCY_EXIT;
5745 element = EL_SIGN_YIN_YANG;
5749 element = EL_WALL_EMERALD;
5753 element = EL_WALL_DIAMOND;
5757 element = EL_WALL_PEARL;
5761 element = EL_WALL_CRYSTAL;
5765 element = EL_INVISIBLE_WALL;
5769 element = EL_INVISIBLE_STEELWALL;
5773 // EL_INVISIBLE_SAND
5776 element = EL_LIGHT_SWITCH;
5780 element = EL_ENVELOPE_1;
5784 if (element >= 0x0117 && element <= 0x036e) // (?)
5785 element = EL_DIAMOND;
5786 else if (element >= 0x042d && element <= 0x0684) // (?)
5787 element = EL_EMERALD;
5788 else if (element >= 0x157c && element <= 0x158b)
5790 else if (element >= 0x1590 && element <= 0x159f)
5791 element = EL_DC_LANDMINE;
5792 else if (element >= 0x16bc && element <= 0x16cb)
5793 element = EL_INVISIBLE_SAND;
5796 Warn("unknown Diamond Caves element 0x%04x", element);
5798 element = EL_UNKNOWN;
5803 return getMappedElement(element);
5806 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5808 byte header[DC_LEVEL_HEADER_SIZE];
5810 int envelope_header_pos = 62;
5811 int envelope_content_pos = 94;
5812 int level_name_pos = 251;
5813 int level_author_pos = 292;
5814 int envelope_header_len;
5815 int envelope_content_len;
5817 int level_author_len;
5819 int num_yamyam_contents;
5822 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5824 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5826 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5828 header[i * 2 + 0] = header_word >> 8;
5829 header[i * 2 + 1] = header_word & 0xff;
5832 // read some values from level header to check level decoding integrity
5833 fieldx = header[6] | (header[7] << 8);
5834 fieldy = header[8] | (header[9] << 8);
5835 num_yamyam_contents = header[60] | (header[61] << 8);
5837 // do some simple sanity checks to ensure that level was correctly decoded
5838 if (fieldx < 1 || fieldx > 256 ||
5839 fieldy < 1 || fieldy > 256 ||
5840 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5842 level->no_valid_file = TRUE;
5844 Warn("cannot decode level from stream -- using empty level");
5849 // maximum envelope header size is 31 bytes
5850 envelope_header_len = header[envelope_header_pos];
5851 // maximum envelope content size is 110 (156?) bytes
5852 envelope_content_len = header[envelope_content_pos];
5854 // maximum level title size is 40 bytes
5855 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5856 // maximum level author size is 30 (51?) bytes
5857 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5861 for (i = 0; i < envelope_header_len; i++)
5862 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5863 level->envelope[0].text[envelope_size++] =
5864 header[envelope_header_pos + 1 + i];
5866 if (envelope_header_len > 0 && envelope_content_len > 0)
5868 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5869 level->envelope[0].text[envelope_size++] = '\n';
5870 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5871 level->envelope[0].text[envelope_size++] = '\n';
5874 for (i = 0; i < envelope_content_len; i++)
5875 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5876 level->envelope[0].text[envelope_size++] =
5877 header[envelope_content_pos + 1 + i];
5879 level->envelope[0].text[envelope_size] = '\0';
5881 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5882 level->envelope[0].ysize = 10;
5883 level->envelope[0].autowrap = TRUE;
5884 level->envelope[0].centered = TRUE;
5886 for (i = 0; i < level_name_len; i++)
5887 level->name[i] = header[level_name_pos + 1 + i];
5888 level->name[level_name_len] = '\0';
5890 for (i = 0; i < level_author_len; i++)
5891 level->author[i] = header[level_author_pos + 1 + i];
5892 level->author[level_author_len] = '\0';
5894 num_yamyam_contents = header[60] | (header[61] << 8);
5895 level->num_yamyam_contents =
5896 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5898 for (i = 0; i < num_yamyam_contents; i++)
5900 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5902 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5903 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5905 if (i < MAX_ELEMENT_CONTENTS)
5906 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5910 fieldx = header[6] | (header[7] << 8);
5911 fieldy = header[8] | (header[9] << 8);
5912 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5913 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5915 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5917 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5918 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5920 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5921 level->field[x][y] = getMappedElement_DC(element_dc);
5924 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5925 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5926 level->field[x][y] = EL_PLAYER_1;
5928 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5929 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5930 level->field[x][y] = EL_PLAYER_2;
5932 level->gems_needed = header[18] | (header[19] << 8);
5934 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5935 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5936 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5937 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5938 level->score[SC_NUT] = header[28] | (header[29] << 8);
5939 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5940 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5941 level->score[SC_BUG] = header[34] | (header[35] << 8);
5942 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5943 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5944 level->score[SC_KEY] = header[40] | (header[41] << 8);
5945 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5947 level->time = header[44] | (header[45] << 8);
5949 level->amoeba_speed = header[46] | (header[47] << 8);
5950 level->time_light = header[48] | (header[49] << 8);
5951 level->time_timegate = header[50] | (header[51] << 8);
5952 level->time_wheel = header[52] | (header[53] << 8);
5953 level->time_magic_wall = header[54] | (header[55] << 8);
5954 level->extra_time = header[56] | (header[57] << 8);
5955 level->shield_normal_time = header[58] | (header[59] << 8);
5957 // shield and extra time elements do not have a score
5958 level->score[SC_SHIELD] = 0;
5959 level->extra_time_score = 0;
5961 // set time for normal and deadly shields to the same value
5962 level->shield_deadly_time = level->shield_normal_time;
5964 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5965 // can slip down from flat walls, like normal walls and steel walls
5966 level->em_slippery_gems = TRUE;
5968 // time score is counted for each 10 seconds left in Diamond Caves levels
5969 level->time_score_base = 10;
5972 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5973 struct LevelFileInfo *level_file_info,
5974 boolean level_info_only)
5976 char *filename = level_file_info->filename;
5978 int num_magic_bytes = 8;
5979 char magic_bytes[num_magic_bytes + 1];
5980 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5982 if (!(file = openFile(filename, MODE_READ)))
5984 level->no_valid_file = TRUE;
5986 if (!level_info_only)
5987 Warn("cannot read level '%s' -- using empty level", filename);
5992 // fseek(file, 0x0000, SEEK_SET);
5994 if (level_file_info->packed)
5996 // read "magic bytes" from start of file
5997 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5998 magic_bytes[0] = '\0';
6000 // check "magic bytes" for correct file format
6001 if (!strPrefix(magic_bytes, "DC2"))
6003 level->no_valid_file = TRUE;
6005 Warn("unknown DC level file '%s' -- using empty level", filename);
6010 if (strPrefix(magic_bytes, "DC2Win95") ||
6011 strPrefix(magic_bytes, "DC2Win98"))
6013 int position_first_level = 0x00fa;
6014 int extra_bytes = 4;
6017 // advance file stream to first level inside the level package
6018 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6020 // each block of level data is followed by block of non-level data
6021 num_levels_to_skip *= 2;
6023 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6024 while (num_levels_to_skip >= 0)
6026 // advance file stream to next level inside the level package
6027 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6029 level->no_valid_file = TRUE;
6031 Warn("cannot fseek in file '%s' -- using empty level", filename);
6036 // skip apparently unused extra bytes following each level
6037 ReadUnusedBytesFromFile(file, extra_bytes);
6039 // read size of next level in level package
6040 skip_bytes = getFile32BitLE(file);
6042 num_levels_to_skip--;
6047 level->no_valid_file = TRUE;
6049 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6055 LoadLevelFromFileStream_DC(file, level);
6061 // ----------------------------------------------------------------------------
6062 // functions for loading SB level
6063 // ----------------------------------------------------------------------------
6065 int getMappedElement_SB(int element_ascii, boolean use_ces)
6073 sb_element_mapping[] =
6075 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6076 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6077 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6078 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6079 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6080 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6081 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6082 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6089 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6090 if (element_ascii == sb_element_mapping[i].ascii)
6091 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6093 return EL_UNDEFINED;
6096 static void SetLevelSettings_SB(struct LevelInfo *level)
6100 level->use_step_counter = TRUE;
6103 level->score[SC_TIME_BONUS] = 0;
6104 level->time_score_base = 1;
6105 level->rate_time_over_score = TRUE;
6108 level->auto_exit_sokoban = TRUE;
6111 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6112 struct LevelFileInfo *level_file_info,
6113 boolean level_info_only)
6115 char *filename = level_file_info->filename;
6116 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6117 char last_comment[MAX_LINE_LEN];
6118 char level_name[MAX_LINE_LEN];
6121 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6122 boolean read_continued_line = FALSE;
6123 boolean reading_playfield = FALSE;
6124 boolean got_valid_playfield_line = FALSE;
6125 boolean invalid_playfield_char = FALSE;
6126 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6127 int file_level_nr = 0;
6129 int x = 0, y = 0; // initialized to make compilers happy
6131 last_comment[0] = '\0';
6132 level_name[0] = '\0';
6134 if (!(file = openFile(filename, MODE_READ)))
6136 level->no_valid_file = TRUE;
6138 if (!level_info_only)
6139 Warn("cannot read level '%s' -- using empty level", filename);
6144 while (!checkEndOfFile(file))
6146 // level successfully read, but next level may follow here
6147 if (!got_valid_playfield_line && reading_playfield)
6149 // read playfield from single level file -- skip remaining file
6150 if (!level_file_info->packed)
6153 if (file_level_nr >= num_levels_to_skip)
6158 last_comment[0] = '\0';
6159 level_name[0] = '\0';
6161 reading_playfield = FALSE;
6164 got_valid_playfield_line = FALSE;
6166 // read next line of input file
6167 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6170 // check if line was completely read and is terminated by line break
6171 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6174 // cut trailing line break (this can be newline and/or carriage return)
6175 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6176 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6179 // copy raw input line for later use (mainly debugging output)
6180 strcpy(line_raw, line);
6182 if (read_continued_line)
6184 // append new line to existing line, if there is enough space
6185 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6186 strcat(previous_line, line_ptr);
6188 strcpy(line, previous_line); // copy storage buffer to line
6190 read_continued_line = FALSE;
6193 // if the last character is '\', continue at next line
6194 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6196 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6197 strcpy(previous_line, line); // copy line to storage buffer
6199 read_continued_line = TRUE;
6205 if (line[0] == '\0')
6208 // extract comment text from comment line
6211 for (line_ptr = line; *line_ptr; line_ptr++)
6212 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6215 strcpy(last_comment, line_ptr);
6220 // extract level title text from line containing level title
6221 if (line[0] == '\'')
6223 strcpy(level_name, &line[1]);
6225 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6226 level_name[strlen(level_name) - 1] = '\0';
6231 // skip lines containing only spaces (or empty lines)
6232 for (line_ptr = line; *line_ptr; line_ptr++)
6233 if (*line_ptr != ' ')
6235 if (*line_ptr == '\0')
6238 // at this point, we have found a line containing part of a playfield
6240 got_valid_playfield_line = TRUE;
6242 if (!reading_playfield)
6244 reading_playfield = TRUE;
6245 invalid_playfield_char = FALSE;
6247 for (x = 0; x < MAX_LEV_FIELDX; x++)
6248 for (y = 0; y < MAX_LEV_FIELDY; y++)
6249 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6254 // start with topmost tile row
6258 // skip playfield line if larger row than allowed
6259 if (y >= MAX_LEV_FIELDY)
6262 // start with leftmost tile column
6265 // read playfield elements from line
6266 for (line_ptr = line; *line_ptr; line_ptr++)
6268 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6270 // stop parsing playfield line if larger column than allowed
6271 if (x >= MAX_LEV_FIELDX)
6274 if (mapped_sb_element == EL_UNDEFINED)
6276 invalid_playfield_char = TRUE;
6281 level->field[x][y] = mapped_sb_element;
6283 // continue with next tile column
6286 level->fieldx = MAX(x, level->fieldx);
6289 if (invalid_playfield_char)
6291 // if first playfield line, treat invalid lines as comment lines
6293 reading_playfield = FALSE;
6298 // continue with next tile row
6306 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6307 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6309 if (!reading_playfield)
6311 level->no_valid_file = TRUE;
6313 Warn("cannot read level '%s' -- using empty level", filename);
6318 if (*level_name != '\0')
6320 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6321 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6323 else if (*last_comment != '\0')
6325 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6326 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6330 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6333 // set all empty fields beyond the border walls to invisible steel wall
6334 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6336 if ((x == 0 || x == level->fieldx - 1 ||
6337 y == 0 || y == level->fieldy - 1) &&
6338 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6339 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6340 level->field, level->fieldx, level->fieldy);
6343 // set special level settings for Sokoban levels
6344 SetLevelSettings_SB(level);
6346 if (load_xsb_to_ces)
6348 // special global settings can now be set in level template
6349 level->use_custom_template = TRUE;
6354 // -------------------------------------------------------------------------
6355 // functions for handling native levels
6356 // -------------------------------------------------------------------------
6358 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6359 struct LevelFileInfo *level_file_info,
6360 boolean level_info_only)
6362 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6363 level->no_valid_file = TRUE;
6366 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6367 struct LevelFileInfo *level_file_info,
6368 boolean level_info_only)
6372 // determine position of requested level inside level package
6373 if (level_file_info->packed)
6374 pos = level_file_info->nr - leveldir_current->first_level;
6376 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6377 level->no_valid_file = TRUE;
6380 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6381 struct LevelFileInfo *level_file_info,
6382 boolean level_info_only)
6384 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6385 level->no_valid_file = TRUE;
6388 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6390 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6391 CopyNativeLevel_RND_to_BD(level);
6392 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6393 CopyNativeLevel_RND_to_EM(level);
6394 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6395 CopyNativeLevel_RND_to_SP(level);
6396 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6397 CopyNativeLevel_RND_to_MM(level);
6400 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6402 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6403 CopyNativeLevel_BD_to_RND(level);
6404 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6405 CopyNativeLevel_EM_to_RND(level);
6406 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6407 CopyNativeLevel_SP_to_RND(level);
6408 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6409 CopyNativeLevel_MM_to_RND(level);
6412 void SaveNativeLevel(struct LevelInfo *level)
6414 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6416 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6417 char *filename = getLevelFilenameFromBasename(basename);
6419 CopyNativeLevel_RND_to_SP(level);
6420 CopyNativeTape_RND_to_SP(level);
6422 SaveNativeLevel_SP(filename);
6427 // ----------------------------------------------------------------------------
6428 // functions for loading generic level
6429 // ----------------------------------------------------------------------------
6431 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6432 struct LevelFileInfo *level_file_info,
6433 boolean level_info_only)
6435 // always start with reliable default values
6436 setLevelInfoToDefaults(level, level_info_only, TRUE);
6438 switch (level_file_info->type)
6440 case LEVEL_FILE_TYPE_RND:
6441 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6444 case LEVEL_FILE_TYPE_EM:
6445 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6446 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6449 case LEVEL_FILE_TYPE_SP:
6450 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6451 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6454 case LEVEL_FILE_TYPE_MM:
6455 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6456 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6459 case LEVEL_FILE_TYPE_DC:
6460 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6463 case LEVEL_FILE_TYPE_SB:
6464 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6468 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6472 // if level file is invalid, restore level structure to default values
6473 if (level->no_valid_file)
6474 setLevelInfoToDefaults(level, level_info_only, FALSE);
6476 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6477 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6479 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6480 CopyNativeLevel_Native_to_RND(level);
6483 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6485 static struct LevelFileInfo level_file_info;
6487 // always start with reliable default values
6488 setFileInfoToDefaults(&level_file_info);
6490 level_file_info.nr = 0; // unknown level number
6491 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6493 setString(&level_file_info.filename, filename);
6495 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6498 static void LoadLevel_InitVersion(struct LevelInfo *level)
6502 if (leveldir_current == NULL) // only when dumping level
6505 // all engine modifications also valid for levels which use latest engine
6506 if (level->game_version < VERSION_IDENT(3,2,0,5))
6508 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6509 level->time_score_base = 10;
6512 if (leveldir_current->latest_engine)
6514 // ---------- use latest game engine --------------------------------------
6516 /* For all levels which are forced to use the latest game engine version
6517 (normally all but user contributed, private and undefined levels), set
6518 the game engine version to the actual version; this allows for actual
6519 corrections in the game engine to take effect for existing, converted
6520 levels (from "classic" or other existing games) to make the emulation
6521 of the corresponding game more accurate, while (hopefully) not breaking
6522 existing levels created from other players. */
6524 level->game_version = GAME_VERSION_ACTUAL;
6526 /* Set special EM style gems behaviour: EM style gems slip down from
6527 normal, steel and growing wall. As this is a more fundamental change,
6528 it seems better to set the default behaviour to "off" (as it is more
6529 natural) and make it configurable in the level editor (as a property
6530 of gem style elements). Already existing converted levels (neither
6531 private nor contributed levels) are changed to the new behaviour. */
6533 if (level->file_version < FILE_VERSION_2_0)
6534 level->em_slippery_gems = TRUE;
6539 // ---------- use game engine the level was created with --------------------
6541 /* For all levels which are not forced to use the latest game engine
6542 version (normally user contributed, private and undefined levels),
6543 use the version of the game engine the levels were created for.
6545 Since 2.0.1, the game engine version is now directly stored
6546 in the level file (chunk "VERS"), so there is no need anymore
6547 to set the game version from the file version (except for old,
6548 pre-2.0 levels, where the game version is still taken from the
6549 file format version used to store the level -- see above). */
6551 // player was faster than enemies in 1.0.0 and before
6552 if (level->file_version == FILE_VERSION_1_0)
6553 for (i = 0; i < MAX_PLAYERS; i++)
6554 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6556 // default behaviour for EM style gems was "slippery" only in 2.0.1
6557 if (level->game_version == VERSION_IDENT(2,0,1,0))
6558 level->em_slippery_gems = TRUE;
6560 // springs could be pushed over pits before (pre-release version) 2.2.0
6561 if (level->game_version < VERSION_IDENT(2,2,0,0))
6562 level->use_spring_bug = TRUE;
6564 if (level->game_version < VERSION_IDENT(3,2,0,5))
6566 // time orb caused limited time in endless time levels before 3.2.0-5
6567 level->use_time_orb_bug = TRUE;
6569 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6570 level->block_snap_field = FALSE;
6572 // extra time score was same value as time left score before 3.2.0-5
6573 level->extra_time_score = level->score[SC_TIME_BONUS];
6576 if (level->game_version < VERSION_IDENT(3,2,0,7))
6578 // default behaviour for snapping was "not continuous" before 3.2.0-7
6579 level->continuous_snapping = FALSE;
6582 // only few elements were able to actively move into acid before 3.1.0
6583 // trigger settings did not exist before 3.1.0; set to default "any"
6584 if (level->game_version < VERSION_IDENT(3,1,0,0))
6586 // correct "can move into acid" settings (all zero in old levels)
6588 level->can_move_into_acid_bits = 0; // nothing can move into acid
6589 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6591 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6592 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6593 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6594 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6596 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6597 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6599 // correct trigger settings (stored as zero == "none" in old levels)
6601 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6603 int element = EL_CUSTOM_START + i;
6604 struct ElementInfo *ei = &element_info[element];
6606 for (j = 0; j < ei->num_change_pages; j++)
6608 struct ElementChangeInfo *change = &ei->change_page[j];
6610 change->trigger_player = CH_PLAYER_ANY;
6611 change->trigger_page = CH_PAGE_ANY;
6616 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6618 int element = EL_CUSTOM_256;
6619 struct ElementInfo *ei = &element_info[element];
6620 struct ElementChangeInfo *change = &ei->change_page[0];
6622 /* This is needed to fix a problem that was caused by a bugfix in function
6623 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6624 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6625 not replace walkable elements, but instead just placed the player on it,
6626 without placing the Sokoban field under the player). Unfortunately, this
6627 breaks "Snake Bite" style levels when the snake is halfway through a door
6628 that just closes (the snake head is still alive and can be moved in this
6629 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6630 player (without Sokoban element) which then gets killed as designed). */
6632 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6633 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6634 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6635 change->target_element = EL_PLAYER_1;
6638 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6639 if (level->game_version < VERSION_IDENT(3,2,5,0))
6641 /* This is needed to fix a problem that was caused by a bugfix in function
6642 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6643 corrects the behaviour when a custom element changes to another custom
6644 element with a higher element number that has change actions defined.
6645 Normally, only one change per frame is allowed for custom elements.
6646 Therefore, it is checked if a custom element already changed in the
6647 current frame; if it did, subsequent changes are suppressed.
6648 Unfortunately, this is only checked for element changes, but not for
6649 change actions, which are still executed. As the function above loops
6650 through all custom elements from lower to higher, an element change
6651 resulting in a lower CE number won't be checked again, while a target
6652 element with a higher number will also be checked, and potential change
6653 actions will get executed for this CE, too (which is wrong), while
6654 further changes are ignored (which is correct). As this bugfix breaks
6655 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6656 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6657 behaviour for existing levels and tapes that make use of this bug */
6659 level->use_action_after_change_bug = TRUE;
6662 // not centering level after relocating player was default only in 3.2.3
6663 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6664 level->shifted_relocation = TRUE;
6666 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6667 if (level->game_version < VERSION_IDENT(3,2,6,0))
6668 level->em_explodes_by_fire = TRUE;
6670 // levels were solved by the first player entering an exit up to 4.1.0.0
6671 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6672 level->solved_by_one_player = TRUE;
6674 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6675 if (level->game_version < VERSION_IDENT(4,1,1,1))
6676 level->use_life_bugs = TRUE;
6678 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6679 if (level->game_version < VERSION_IDENT(4,1,1,1))
6680 level->sb_objects_needed = FALSE;
6682 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6683 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6684 level->finish_dig_collect = FALSE;
6686 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6687 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6688 level->keep_walkable_ce = TRUE;
6691 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6693 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6696 // check if this level is (not) a Sokoban level
6697 for (y = 0; y < level->fieldy; y++)
6698 for (x = 0; x < level->fieldx; x++)
6699 if (!IS_SB_ELEMENT(Tile[x][y]))
6700 is_sokoban_level = FALSE;
6702 if (is_sokoban_level)
6704 // set special level settings for Sokoban levels
6705 SetLevelSettings_SB(level);
6709 static void LoadLevel_InitSettings(struct LevelInfo *level)
6711 // adjust level settings for (non-native) Sokoban-style levels
6712 LoadLevel_InitSettings_SB(level);
6714 // rename levels with title "nameless level" or if renaming is forced
6715 if (leveldir_current->empty_level_name != NULL &&
6716 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6717 leveldir_current->force_level_name))
6718 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6719 leveldir_current->empty_level_name, level_nr);
6722 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6726 // map elements that have changed in newer versions
6727 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6728 level->game_version);
6729 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6730 for (x = 0; x < 3; x++)
6731 for (y = 0; y < 3; y++)
6732 level->yamyam_content[i].e[x][y] =
6733 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6734 level->game_version);
6738 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6742 // map custom element change events that have changed in newer versions
6743 // (these following values were accidentally changed in version 3.0.1)
6744 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6745 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6747 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6749 int element = EL_CUSTOM_START + i;
6751 // order of checking and copying events to be mapped is important
6752 // (do not change the start and end value -- they are constant)
6753 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6755 if (HAS_CHANGE_EVENT(element, j - 2))
6757 SET_CHANGE_EVENT(element, j - 2, FALSE);
6758 SET_CHANGE_EVENT(element, j, TRUE);
6762 // order of checking and copying events to be mapped is important
6763 // (do not change the start and end value -- they are constant)
6764 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6766 if (HAS_CHANGE_EVENT(element, j - 1))
6768 SET_CHANGE_EVENT(element, j - 1, FALSE);
6769 SET_CHANGE_EVENT(element, j, TRUE);
6775 // initialize "can_change" field for old levels with only one change page
6776 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6778 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6780 int element = EL_CUSTOM_START + i;
6782 if (CAN_CHANGE(element))
6783 element_info[element].change->can_change = TRUE;
6787 // correct custom element values (for old levels without these options)
6788 if (level->game_version < VERSION_IDENT(3,1,1,0))
6790 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6792 int element = EL_CUSTOM_START + i;
6793 struct ElementInfo *ei = &element_info[element];
6795 if (ei->access_direction == MV_NO_DIRECTION)
6796 ei->access_direction = MV_ALL_DIRECTIONS;
6800 // correct custom element values (fix invalid values for all versions)
6803 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6805 int element = EL_CUSTOM_START + i;
6806 struct ElementInfo *ei = &element_info[element];
6808 for (j = 0; j < ei->num_change_pages; j++)
6810 struct ElementChangeInfo *change = &ei->change_page[j];
6812 if (change->trigger_player == CH_PLAYER_NONE)
6813 change->trigger_player = CH_PLAYER_ANY;
6815 if (change->trigger_side == CH_SIDE_NONE)
6816 change->trigger_side = CH_SIDE_ANY;
6821 // initialize "can_explode" field for old levels which did not store this
6822 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6823 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6825 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6827 int element = EL_CUSTOM_START + i;
6829 if (EXPLODES_1X1_OLD(element))
6830 element_info[element].explosion_type = EXPLODES_1X1;
6832 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6833 EXPLODES_SMASHED(element) ||
6834 EXPLODES_IMPACT(element)));
6838 // correct previously hard-coded move delay values for maze runner style
6839 if (level->game_version < VERSION_IDENT(3,1,1,0))
6841 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6843 int element = EL_CUSTOM_START + i;
6845 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6847 // previously hard-coded and therefore ignored
6848 element_info[element].move_delay_fixed = 9;
6849 element_info[element].move_delay_random = 0;
6854 // set some other uninitialized values of custom elements in older levels
6855 if (level->game_version < VERSION_IDENT(3,1,0,0))
6857 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6859 int element = EL_CUSTOM_START + i;
6861 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6863 element_info[element].explosion_delay = 17;
6864 element_info[element].ignition_delay = 8;
6868 // set mouse click change events to work for left/middle/right mouse button
6869 if (level->game_version < VERSION_IDENT(4,2,3,0))
6871 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6873 int element = EL_CUSTOM_START + i;
6874 struct ElementInfo *ei = &element_info[element];
6876 for (j = 0; j < ei->num_change_pages; j++)
6878 struct ElementChangeInfo *change = &ei->change_page[j];
6880 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6881 change->has_event[CE_PRESSED_BY_MOUSE] ||
6882 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6883 change->has_event[CE_MOUSE_PRESSED_ON_X])
6884 change->trigger_side = CH_SIDE_ANY;
6890 static void LoadLevel_InitElements(struct LevelInfo *level)
6892 LoadLevel_InitStandardElements(level);
6894 if (level->file_has_custom_elements)
6895 LoadLevel_InitCustomElements(level);
6897 // initialize element properties for level editor etc.
6898 InitElementPropertiesEngine(level->game_version);
6899 InitElementPropertiesGfxElement();
6902 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6906 // map elements that have changed in newer versions
6907 for (y = 0; y < level->fieldy; y++)
6908 for (x = 0; x < level->fieldx; x++)
6909 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6910 level->game_version);
6912 // clear unused playfield data (nicer if level gets resized in editor)
6913 for (x = 0; x < MAX_LEV_FIELDX; x++)
6914 for (y = 0; y < MAX_LEV_FIELDY; y++)
6915 if (x >= level->fieldx || y >= level->fieldy)
6916 level->field[x][y] = EL_EMPTY;
6918 // copy elements to runtime playfield array
6919 for (x = 0; x < MAX_LEV_FIELDX; x++)
6920 for (y = 0; y < MAX_LEV_FIELDY; y++)
6921 Tile[x][y] = level->field[x][y];
6923 // initialize level size variables for faster access
6924 lev_fieldx = level->fieldx;
6925 lev_fieldy = level->fieldy;
6927 // determine border element for this level
6928 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6929 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6934 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6936 struct LevelFileInfo *level_file_info = &level->file_info;
6938 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6939 CopyNativeLevel_RND_to_Native(level);
6942 static void LoadLevelTemplate_LoadAndInit(void)
6944 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6946 LoadLevel_InitVersion(&level_template);
6947 LoadLevel_InitElements(&level_template);
6948 LoadLevel_InitSettings(&level_template);
6950 ActivateLevelTemplate();
6953 void LoadLevelTemplate(int nr)
6955 if (!fileExists(getGlobalLevelTemplateFilename()))
6957 Warn("no level template found for this level");
6962 setLevelFileInfo(&level_template.file_info, nr);
6964 LoadLevelTemplate_LoadAndInit();
6967 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6969 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6971 LoadLevelTemplate_LoadAndInit();
6974 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6976 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6978 if (level.use_custom_template)
6980 if (network_level != NULL)
6981 LoadNetworkLevelTemplate(network_level);
6983 LoadLevelTemplate(-1);
6986 LoadLevel_InitVersion(&level);
6987 LoadLevel_InitElements(&level);
6988 LoadLevel_InitPlayfield(&level);
6989 LoadLevel_InitSettings(&level);
6991 LoadLevel_InitNativeEngines(&level);
6994 void LoadLevel(int nr)
6996 SetLevelSetInfo(leveldir_current->identifier, nr);
6998 setLevelFileInfo(&level.file_info, nr);
7000 LoadLevel_LoadAndInit(NULL);
7003 void LoadLevelInfoOnly(int nr)
7005 setLevelFileInfo(&level.file_info, nr);
7007 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7010 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7012 SetLevelSetInfo(network_level->leveldir_identifier,
7013 network_level->file_info.nr);
7015 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7017 LoadLevel_LoadAndInit(network_level);
7020 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7024 chunk_size += putFileVersion(file, level->file_version);
7025 chunk_size += putFileVersion(file, level->game_version);
7030 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7034 chunk_size += putFile16BitBE(file, level->creation_date.year);
7035 chunk_size += putFile8Bit(file, level->creation_date.month);
7036 chunk_size += putFile8Bit(file, level->creation_date.day);
7041 #if ENABLE_HISTORIC_CHUNKS
7042 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7046 putFile8Bit(file, level->fieldx);
7047 putFile8Bit(file, level->fieldy);
7049 putFile16BitBE(file, level->time);
7050 putFile16BitBE(file, level->gems_needed);
7052 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7053 putFile8Bit(file, level->name[i]);
7055 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7056 putFile8Bit(file, level->score[i]);
7058 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7059 for (y = 0; y < 3; y++)
7060 for (x = 0; x < 3; x++)
7061 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7062 level->yamyam_content[i].e[x][y]));
7063 putFile8Bit(file, level->amoeba_speed);
7064 putFile8Bit(file, level->time_magic_wall);
7065 putFile8Bit(file, level->time_wheel);
7066 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7067 level->amoeba_content));
7068 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7069 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7070 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7071 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7073 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7075 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7076 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7077 putFile32BitBE(file, level->can_move_into_acid_bits);
7078 putFile8Bit(file, level->dont_collide_with_bits);
7080 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7081 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7083 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7084 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7085 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7087 putFile8Bit(file, level->game_engine_type);
7089 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7093 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7098 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7099 chunk_size += putFile8Bit(file, level->name[i]);
7104 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7109 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7110 chunk_size += putFile8Bit(file, level->author[i]);
7115 #if ENABLE_HISTORIC_CHUNKS
7116 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7121 for (y = 0; y < level->fieldy; y++)
7122 for (x = 0; x < level->fieldx; x++)
7123 if (level->encoding_16bit_field)
7124 chunk_size += putFile16BitBE(file, level->field[x][y]);
7126 chunk_size += putFile8Bit(file, level->field[x][y]);
7132 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7137 for (y = 0; y < level->fieldy; y++)
7138 for (x = 0; x < level->fieldx; x++)
7139 chunk_size += putFile16BitBE(file, level->field[x][y]);
7144 #if ENABLE_HISTORIC_CHUNKS
7145 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7149 putFile8Bit(file, EL_YAMYAM);
7150 putFile8Bit(file, level->num_yamyam_contents);
7151 putFile8Bit(file, 0);
7152 putFile8Bit(file, 0);
7154 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7155 for (y = 0; y < 3; y++)
7156 for (x = 0; x < 3; x++)
7157 if (level->encoding_16bit_field)
7158 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7160 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7164 #if ENABLE_HISTORIC_CHUNKS
7165 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7168 int num_contents, content_xsize, content_ysize;
7169 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7171 if (element == EL_YAMYAM)
7173 num_contents = level->num_yamyam_contents;
7177 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7178 for (y = 0; y < 3; y++)
7179 for (x = 0; x < 3; x++)
7180 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7182 else if (element == EL_BD_AMOEBA)
7188 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7189 for (y = 0; y < 3; y++)
7190 for (x = 0; x < 3; x++)
7191 content_array[i][x][y] = EL_EMPTY;
7192 content_array[0][0][0] = level->amoeba_content;
7196 // chunk header already written -- write empty chunk data
7197 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7199 Warn("cannot save content for element '%d'", element);
7204 putFile16BitBE(file, element);
7205 putFile8Bit(file, num_contents);
7206 putFile8Bit(file, content_xsize);
7207 putFile8Bit(file, content_ysize);
7209 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7211 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7212 for (y = 0; y < 3; y++)
7213 for (x = 0; x < 3; x++)
7214 putFile16BitBE(file, content_array[i][x][y]);
7218 #if ENABLE_HISTORIC_CHUNKS
7219 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7221 int envelope_nr = element - EL_ENVELOPE_1;
7222 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7226 chunk_size += putFile16BitBE(file, element);
7227 chunk_size += putFile16BitBE(file, envelope_len);
7228 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7229 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7231 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7232 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7234 for (i = 0; i < envelope_len; i++)
7235 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7241 #if ENABLE_HISTORIC_CHUNKS
7242 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7243 int num_changed_custom_elements)
7247 putFile16BitBE(file, num_changed_custom_elements);
7249 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7251 int element = EL_CUSTOM_START + i;
7253 struct ElementInfo *ei = &element_info[element];
7255 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7257 if (check < num_changed_custom_elements)
7259 putFile16BitBE(file, element);
7260 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7267 if (check != num_changed_custom_elements) // should not happen
7268 Warn("inconsistent number of custom element properties");
7272 #if ENABLE_HISTORIC_CHUNKS
7273 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7274 int num_changed_custom_elements)
7278 putFile16BitBE(file, num_changed_custom_elements);
7280 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7282 int element = EL_CUSTOM_START + i;
7284 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7286 if (check < num_changed_custom_elements)
7288 putFile16BitBE(file, element);
7289 putFile16BitBE(file, element_info[element].change->target_element);
7296 if (check != num_changed_custom_elements) // should not happen
7297 Warn("inconsistent number of custom target elements");
7301 #if ENABLE_HISTORIC_CHUNKS
7302 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7303 int num_changed_custom_elements)
7305 int i, j, x, y, check = 0;
7307 putFile16BitBE(file, num_changed_custom_elements);
7309 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7311 int element = EL_CUSTOM_START + i;
7312 struct ElementInfo *ei = &element_info[element];
7314 if (ei->modified_settings)
7316 if (check < num_changed_custom_elements)
7318 putFile16BitBE(file, element);
7320 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7321 putFile8Bit(file, ei->description[j]);
7323 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7325 // some free bytes for future properties and padding
7326 WriteUnusedBytesToFile(file, 7);
7328 putFile8Bit(file, ei->use_gfx_element);
7329 putFile16BitBE(file, ei->gfx_element_initial);
7331 putFile8Bit(file, ei->collect_score_initial);
7332 putFile8Bit(file, ei->collect_count_initial);
7334 putFile16BitBE(file, ei->push_delay_fixed);
7335 putFile16BitBE(file, ei->push_delay_random);
7336 putFile16BitBE(file, ei->move_delay_fixed);
7337 putFile16BitBE(file, ei->move_delay_random);
7339 putFile16BitBE(file, ei->move_pattern);
7340 putFile8Bit(file, ei->move_direction_initial);
7341 putFile8Bit(file, ei->move_stepsize);
7343 for (y = 0; y < 3; y++)
7344 for (x = 0; x < 3; x++)
7345 putFile16BitBE(file, ei->content.e[x][y]);
7347 putFile32BitBE(file, ei->change->events);
7349 putFile16BitBE(file, ei->change->target_element);
7351 putFile16BitBE(file, ei->change->delay_fixed);
7352 putFile16BitBE(file, ei->change->delay_random);
7353 putFile16BitBE(file, ei->change->delay_frames);
7355 putFile16BitBE(file, ei->change->initial_trigger_element);
7357 putFile8Bit(file, ei->change->explode);
7358 putFile8Bit(file, ei->change->use_target_content);
7359 putFile8Bit(file, ei->change->only_if_complete);
7360 putFile8Bit(file, ei->change->use_random_replace);
7362 putFile8Bit(file, ei->change->random_percentage);
7363 putFile8Bit(file, ei->change->replace_when);
7365 for (y = 0; y < 3; y++)
7366 for (x = 0; x < 3; x++)
7367 putFile16BitBE(file, ei->change->content.e[x][y]);
7369 putFile8Bit(file, ei->slippery_type);
7371 // some free bytes for future properties and padding
7372 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7379 if (check != num_changed_custom_elements) // should not happen
7380 Warn("inconsistent number of custom element properties");
7384 #if ENABLE_HISTORIC_CHUNKS
7385 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7387 struct ElementInfo *ei = &element_info[element];
7390 // ---------- custom element base property values (96 bytes) ----------------
7392 putFile16BitBE(file, element);
7394 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7395 putFile8Bit(file, ei->description[i]);
7397 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7399 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7401 putFile8Bit(file, ei->num_change_pages);
7403 putFile16BitBE(file, ei->ce_value_fixed_initial);
7404 putFile16BitBE(file, ei->ce_value_random_initial);
7405 putFile8Bit(file, ei->use_last_ce_value);
7407 putFile8Bit(file, ei->use_gfx_element);
7408 putFile16BitBE(file, ei->gfx_element_initial);
7410 putFile8Bit(file, ei->collect_score_initial);
7411 putFile8Bit(file, ei->collect_count_initial);
7413 putFile8Bit(file, ei->drop_delay_fixed);
7414 putFile8Bit(file, ei->push_delay_fixed);
7415 putFile8Bit(file, ei->drop_delay_random);
7416 putFile8Bit(file, ei->push_delay_random);
7417 putFile16BitBE(file, ei->move_delay_fixed);
7418 putFile16BitBE(file, ei->move_delay_random);
7420 // bits 0 - 15 of "move_pattern" ...
7421 putFile16BitBE(file, ei->move_pattern & 0xffff);
7422 putFile8Bit(file, ei->move_direction_initial);
7423 putFile8Bit(file, ei->move_stepsize);
7425 putFile8Bit(file, ei->slippery_type);
7427 for (y = 0; y < 3; y++)
7428 for (x = 0; x < 3; x++)
7429 putFile16BitBE(file, ei->content.e[x][y]);
7431 putFile16BitBE(file, ei->move_enter_element);
7432 putFile16BitBE(file, ei->move_leave_element);
7433 putFile8Bit(file, ei->move_leave_type);
7435 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7436 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7438 putFile8Bit(file, ei->access_direction);
7440 putFile8Bit(file, ei->explosion_delay);
7441 putFile8Bit(file, ei->ignition_delay);
7442 putFile8Bit(file, ei->explosion_type);
7444 // some free bytes for future custom property values and padding
7445 WriteUnusedBytesToFile(file, 1);
7447 // ---------- change page property values (48 bytes) ------------------------
7449 for (i = 0; i < ei->num_change_pages; i++)
7451 struct ElementChangeInfo *change = &ei->change_page[i];
7452 unsigned int event_bits;
7454 // bits 0 - 31 of "has_event[]" ...
7456 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7457 if (change->has_event[j])
7458 event_bits |= (1u << j);
7459 putFile32BitBE(file, event_bits);
7461 putFile16BitBE(file, change->target_element);
7463 putFile16BitBE(file, change->delay_fixed);
7464 putFile16BitBE(file, change->delay_random);
7465 putFile16BitBE(file, change->delay_frames);
7467 putFile16BitBE(file, change->initial_trigger_element);
7469 putFile8Bit(file, change->explode);
7470 putFile8Bit(file, change->use_target_content);
7471 putFile8Bit(file, change->only_if_complete);
7472 putFile8Bit(file, change->use_random_replace);
7474 putFile8Bit(file, change->random_percentage);
7475 putFile8Bit(file, change->replace_when);
7477 for (y = 0; y < 3; y++)
7478 for (x = 0; x < 3; x++)
7479 putFile16BitBE(file, change->target_content.e[x][y]);
7481 putFile8Bit(file, change->can_change);
7483 putFile8Bit(file, change->trigger_side);
7485 putFile8Bit(file, change->trigger_player);
7486 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7487 log_2(change->trigger_page)));
7489 putFile8Bit(file, change->has_action);
7490 putFile8Bit(file, change->action_type);
7491 putFile8Bit(file, change->action_mode);
7492 putFile16BitBE(file, change->action_arg);
7494 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7496 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7497 if (change->has_event[j])
7498 event_bits |= (1u << (j - 32));
7499 putFile8Bit(file, event_bits);
7504 #if ENABLE_HISTORIC_CHUNKS
7505 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7507 struct ElementInfo *ei = &element_info[element];
7508 struct ElementGroupInfo *group = ei->group;
7511 putFile16BitBE(file, element);
7513 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7514 putFile8Bit(file, ei->description[i]);
7516 putFile8Bit(file, group->num_elements);
7518 putFile8Bit(file, ei->use_gfx_element);
7519 putFile16BitBE(file, ei->gfx_element_initial);
7521 putFile8Bit(file, group->choice_mode);
7523 // some free bytes for future values and padding
7524 WriteUnusedBytesToFile(file, 3);
7526 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7527 putFile16BitBE(file, group->element[i]);
7531 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7532 boolean write_element)
7534 int save_type = entry->save_type;
7535 int data_type = entry->data_type;
7536 int conf_type = entry->conf_type;
7537 int byte_mask = conf_type & CONF_MASK_BYTES;
7538 int element = entry->element;
7539 int default_value = entry->default_value;
7541 boolean modified = FALSE;
7543 if (byte_mask != CONF_MASK_MULTI_BYTES)
7545 void *value_ptr = entry->value;
7546 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7549 // check if any settings have been modified before saving them
7550 if (value != default_value)
7553 // do not save if explicitly told or if unmodified default settings
7554 if ((save_type == SAVE_CONF_NEVER) ||
7555 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7559 num_bytes += putFile16BitBE(file, element);
7561 num_bytes += putFile8Bit(file, conf_type);
7562 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7563 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7564 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7567 else if (data_type == TYPE_STRING)
7569 char *default_string = entry->default_string;
7570 char *string = (char *)(entry->value);
7571 int string_length = strlen(string);
7574 // check if any settings have been modified before saving them
7575 if (!strEqual(string, default_string))
7578 // do not save if explicitly told or if unmodified default settings
7579 if ((save_type == SAVE_CONF_NEVER) ||
7580 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7584 num_bytes += putFile16BitBE(file, element);
7586 num_bytes += putFile8Bit(file, conf_type);
7587 num_bytes += putFile16BitBE(file, string_length);
7589 for (i = 0; i < string_length; i++)
7590 num_bytes += putFile8Bit(file, string[i]);
7592 else if (data_type == TYPE_ELEMENT_LIST)
7594 int *element_array = (int *)(entry->value);
7595 int num_elements = *(int *)(entry->num_entities);
7598 // check if any settings have been modified before saving them
7599 for (i = 0; i < num_elements; i++)
7600 if (element_array[i] != default_value)
7603 // do not save if explicitly told or if unmodified default settings
7604 if ((save_type == SAVE_CONF_NEVER) ||
7605 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7609 num_bytes += putFile16BitBE(file, element);
7611 num_bytes += putFile8Bit(file, conf_type);
7612 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7614 for (i = 0; i < num_elements; i++)
7615 num_bytes += putFile16BitBE(file, element_array[i]);
7617 else if (data_type == TYPE_CONTENT_LIST)
7619 struct Content *content = (struct Content *)(entry->value);
7620 int num_contents = *(int *)(entry->num_entities);
7623 // check if any settings have been modified before saving them
7624 for (i = 0; i < num_contents; i++)
7625 for (y = 0; y < 3; y++)
7626 for (x = 0; x < 3; x++)
7627 if (content[i].e[x][y] != default_value)
7630 // do not save if explicitly told or if unmodified default settings
7631 if ((save_type == SAVE_CONF_NEVER) ||
7632 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7636 num_bytes += putFile16BitBE(file, element);
7638 num_bytes += putFile8Bit(file, conf_type);
7639 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7641 for (i = 0; i < num_contents; i++)
7642 for (y = 0; y < 3; y++)
7643 for (x = 0; x < 3; x++)
7644 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7650 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7655 li = *level; // copy level data into temporary buffer
7657 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7658 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7663 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7668 li = *level; // copy level data into temporary buffer
7670 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7671 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7676 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7678 int envelope_nr = element - EL_ENVELOPE_1;
7682 chunk_size += putFile16BitBE(file, element);
7684 // copy envelope data into temporary buffer
7685 xx_envelope = level->envelope[envelope_nr];
7687 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7688 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7693 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7695 struct ElementInfo *ei = &element_info[element];
7699 chunk_size += putFile16BitBE(file, element);
7701 xx_ei = *ei; // copy element data into temporary buffer
7703 // set default description string for this specific element
7704 strcpy(xx_default_description, getDefaultElementDescription(ei));
7706 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7707 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7709 for (i = 0; i < ei->num_change_pages; i++)
7711 struct ElementChangeInfo *change = &ei->change_page[i];
7713 xx_current_change_page = i;
7715 xx_change = *change; // copy change data into temporary buffer
7718 setEventBitsFromEventFlags(change);
7720 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7721 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7728 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7730 struct ElementInfo *ei = &element_info[element];
7731 struct ElementGroupInfo *group = ei->group;
7735 chunk_size += putFile16BitBE(file, element);
7737 xx_ei = *ei; // copy element data into temporary buffer
7738 xx_group = *group; // copy group data into temporary buffer
7740 // set default description string for this specific element
7741 strcpy(xx_default_description, getDefaultElementDescription(ei));
7743 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7744 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7749 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7751 struct ElementInfo *ei = &element_info[element];
7755 chunk_size += putFile16BitBE(file, element);
7757 xx_ei = *ei; // copy element data into temporary buffer
7759 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7760 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7765 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7766 boolean save_as_template)
7772 if (!(file = fopen(filename, MODE_WRITE)))
7774 Warn("cannot save level file '%s'", filename);
7779 level->file_version = FILE_VERSION_ACTUAL;
7780 level->game_version = GAME_VERSION_ACTUAL;
7782 level->creation_date = getCurrentDate();
7784 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7785 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7787 chunk_size = SaveLevel_VERS(NULL, level);
7788 putFileChunkBE(file, "VERS", chunk_size);
7789 SaveLevel_VERS(file, level);
7791 chunk_size = SaveLevel_DATE(NULL, level);
7792 putFileChunkBE(file, "DATE", chunk_size);
7793 SaveLevel_DATE(file, level);
7795 chunk_size = SaveLevel_NAME(NULL, level);
7796 putFileChunkBE(file, "NAME", chunk_size);
7797 SaveLevel_NAME(file, level);
7799 chunk_size = SaveLevel_AUTH(NULL, level);
7800 putFileChunkBE(file, "AUTH", chunk_size);
7801 SaveLevel_AUTH(file, level);
7803 chunk_size = SaveLevel_INFO(NULL, level);
7804 putFileChunkBE(file, "INFO", chunk_size);
7805 SaveLevel_INFO(file, level);
7807 chunk_size = SaveLevel_BODY(NULL, level);
7808 putFileChunkBE(file, "BODY", chunk_size);
7809 SaveLevel_BODY(file, level);
7811 chunk_size = SaveLevel_ELEM(NULL, level);
7812 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7814 putFileChunkBE(file, "ELEM", chunk_size);
7815 SaveLevel_ELEM(file, level);
7818 for (i = 0; i < NUM_ENVELOPES; i++)
7820 int element = EL_ENVELOPE_1 + i;
7822 chunk_size = SaveLevel_NOTE(NULL, level, element);
7823 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7825 putFileChunkBE(file, "NOTE", chunk_size);
7826 SaveLevel_NOTE(file, level, element);
7830 // if not using template level, check for non-default custom/group elements
7831 if (!level->use_custom_template || save_as_template)
7833 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7835 int element = EL_CUSTOM_START + i;
7837 chunk_size = SaveLevel_CUSX(NULL, level, element);
7838 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7840 putFileChunkBE(file, "CUSX", chunk_size);
7841 SaveLevel_CUSX(file, level, element);
7845 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7847 int element = EL_GROUP_START + i;
7849 chunk_size = SaveLevel_GRPX(NULL, level, element);
7850 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7852 putFileChunkBE(file, "GRPX", chunk_size);
7853 SaveLevel_GRPX(file, level, element);
7857 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7859 int element = GET_EMPTY_ELEMENT(i);
7861 chunk_size = SaveLevel_EMPX(NULL, level, element);
7862 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7864 putFileChunkBE(file, "EMPX", chunk_size);
7865 SaveLevel_EMPX(file, level, element);
7872 SetFilePermissions(filename, PERMS_PRIVATE);
7875 void SaveLevel(int nr)
7877 char *filename = getDefaultLevelFilename(nr);
7879 SaveLevelFromFilename(&level, filename, FALSE);
7882 void SaveLevelTemplate(void)
7884 char *filename = getLocalLevelTemplateFilename();
7886 SaveLevelFromFilename(&level, filename, TRUE);
7889 boolean SaveLevelChecked(int nr)
7891 char *filename = getDefaultLevelFilename(nr);
7892 boolean new_level = !fileExists(filename);
7893 boolean level_saved = FALSE;
7895 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7900 Request("Level saved!", REQ_CONFIRM);
7908 void DumpLevel(struct LevelInfo *level)
7910 if (level->no_level_file || level->no_valid_file)
7912 Warn("cannot dump -- no valid level file found");
7918 Print("Level xxx (file version %08d, game version %08d)\n",
7919 level->file_version, level->game_version);
7922 Print("Level author: '%s'\n", level->author);
7923 Print("Level title: '%s'\n", level->name);
7925 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7927 Print("Level time: %d seconds\n", level->time);
7928 Print("Gems needed: %d\n", level->gems_needed);
7930 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7931 Print("Time for wheel: %d seconds\n", level->time_wheel);
7932 Print("Time for light: %d seconds\n", level->time_light);
7933 Print("Time for timegate: %d seconds\n", level->time_timegate);
7935 Print("Amoeba speed: %d\n", level->amoeba_speed);
7938 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7939 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7940 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7941 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7942 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7943 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7949 for (i = 0; i < NUM_ENVELOPES; i++)
7951 char *text = level->envelope[i].text;
7952 int text_len = strlen(text);
7953 boolean has_text = FALSE;
7955 for (j = 0; j < text_len; j++)
7956 if (text[j] != ' ' && text[j] != '\n')
7962 Print("Envelope %d:\n'%s'\n", i + 1, text);
7970 void DumpLevels(void)
7972 static LevelDirTree *dumplevel_leveldir = NULL;
7974 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7975 global.dumplevel_leveldir);
7977 if (dumplevel_leveldir == NULL)
7978 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7980 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7981 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7982 Fail("no such level number: %d", global.dumplevel_level_nr);
7984 leveldir_current = dumplevel_leveldir;
7986 LoadLevel(global.dumplevel_level_nr);
7993 // ============================================================================
7994 // tape file functions
7995 // ============================================================================
7997 static void setTapeInfoToDefaults(void)
8001 // always start with reliable default values (empty tape)
8004 // default values (also for pre-1.2 tapes) with only the first player
8005 tape.player_participates[0] = TRUE;
8006 for (i = 1; i < MAX_PLAYERS; i++)
8007 tape.player_participates[i] = FALSE;
8009 // at least one (default: the first) player participates in every tape
8010 tape.num_participating_players = 1;
8012 tape.property_bits = TAPE_PROPERTY_NONE;
8014 tape.level_nr = level_nr;
8016 tape.changed = FALSE;
8017 tape.solved = FALSE;
8019 tape.recording = FALSE;
8020 tape.playing = FALSE;
8021 tape.pausing = FALSE;
8023 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8024 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8026 tape.no_info_chunk = TRUE;
8027 tape.no_valid_file = FALSE;
8030 static int getTapePosSize(struct TapeInfo *tape)
8032 int tape_pos_size = 0;
8034 if (tape->use_key_actions)
8035 tape_pos_size += tape->num_participating_players;
8037 if (tape->use_mouse_actions)
8038 tape_pos_size += 3; // x and y position and mouse button mask
8040 tape_pos_size += 1; // tape action delay value
8042 return tape_pos_size;
8045 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8047 tape->use_key_actions = FALSE;
8048 tape->use_mouse_actions = FALSE;
8050 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8051 tape->use_key_actions = TRUE;
8053 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8054 tape->use_mouse_actions = TRUE;
8057 static int getTapeActionValue(struct TapeInfo *tape)
8059 return (tape->use_key_actions &&
8060 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8061 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8062 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8063 TAPE_ACTIONS_DEFAULT);
8066 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8068 tape->file_version = getFileVersion(file);
8069 tape->game_version = getFileVersion(file);
8074 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8078 tape->random_seed = getFile32BitBE(file);
8079 tape->date = getFile32BitBE(file);
8080 tape->length = getFile32BitBE(file);
8082 // read header fields that are new since version 1.2
8083 if (tape->file_version >= FILE_VERSION_1_2)
8085 byte store_participating_players = getFile8Bit(file);
8088 // since version 1.2, tapes store which players participate in the tape
8089 tape->num_participating_players = 0;
8090 for (i = 0; i < MAX_PLAYERS; i++)
8092 tape->player_participates[i] = FALSE;
8094 if (store_participating_players & (1 << i))
8096 tape->player_participates[i] = TRUE;
8097 tape->num_participating_players++;
8101 setTapeActionFlags(tape, getFile8Bit(file));
8103 tape->property_bits = getFile8Bit(file);
8104 tape->solved = getFile8Bit(file);
8106 engine_version = getFileVersion(file);
8107 if (engine_version > 0)
8108 tape->engine_version = engine_version;
8110 tape->engine_version = tape->game_version;
8116 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8118 tape->scr_fieldx = getFile8Bit(file);
8119 tape->scr_fieldy = getFile8Bit(file);
8124 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8126 char *level_identifier = NULL;
8127 int level_identifier_size;
8130 tape->no_info_chunk = FALSE;
8132 level_identifier_size = getFile16BitBE(file);
8134 level_identifier = checked_malloc(level_identifier_size);
8136 for (i = 0; i < level_identifier_size; i++)
8137 level_identifier[i] = getFile8Bit(file);
8139 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8140 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8142 checked_free(level_identifier);
8144 tape->level_nr = getFile16BitBE(file);
8146 chunk_size = 2 + level_identifier_size + 2;
8151 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8154 int tape_pos_size = getTapePosSize(tape);
8155 int chunk_size_expected = tape_pos_size * tape->length;
8157 if (chunk_size_expected != chunk_size)
8159 ReadUnusedBytesFromFile(file, chunk_size);
8160 return chunk_size_expected;
8163 for (i = 0; i < tape->length; i++)
8165 if (i >= MAX_TAPE_LEN)
8167 Warn("tape truncated -- size exceeds maximum tape size %d",
8170 // tape too large; read and ignore remaining tape data from this chunk
8171 for (;i < tape->length; i++)
8172 ReadUnusedBytesFromFile(file, tape_pos_size);
8177 if (tape->use_key_actions)
8179 for (j = 0; j < MAX_PLAYERS; j++)
8181 tape->pos[i].action[j] = MV_NONE;
8183 if (tape->player_participates[j])
8184 tape->pos[i].action[j] = getFile8Bit(file);
8188 if (tape->use_mouse_actions)
8190 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8191 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8192 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8195 tape->pos[i].delay = getFile8Bit(file);
8197 if (tape->file_version == FILE_VERSION_1_0)
8199 // eliminate possible diagonal moves in old tapes
8200 // this is only for backward compatibility
8202 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8203 byte action = tape->pos[i].action[0];
8204 int k, num_moves = 0;
8206 for (k = 0; k < 4; k++)
8208 if (action & joy_dir[k])
8210 tape->pos[i + num_moves].action[0] = joy_dir[k];
8212 tape->pos[i + num_moves].delay = 0;
8221 tape->length += num_moves;
8224 else if (tape->file_version < FILE_VERSION_2_0)
8226 // convert pre-2.0 tapes to new tape format
8228 if (tape->pos[i].delay > 1)
8231 tape->pos[i + 1] = tape->pos[i];
8232 tape->pos[i + 1].delay = 1;
8235 for (j = 0; j < MAX_PLAYERS; j++)
8236 tape->pos[i].action[j] = MV_NONE;
8237 tape->pos[i].delay--;
8244 if (checkEndOfFile(file))
8248 if (i != tape->length)
8249 chunk_size = tape_pos_size * i;
8254 static void LoadTape_SokobanSolution(char *filename)
8257 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8259 if (!(file = openFile(filename, MODE_READ)))
8261 tape.no_valid_file = TRUE;
8266 while (!checkEndOfFile(file))
8268 unsigned char c = getByteFromFile(file);
8270 if (checkEndOfFile(file))
8277 tape.pos[tape.length].action[0] = MV_UP;
8278 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8284 tape.pos[tape.length].action[0] = MV_DOWN;
8285 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8291 tape.pos[tape.length].action[0] = MV_LEFT;
8292 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8298 tape.pos[tape.length].action[0] = MV_RIGHT;
8299 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8307 // ignore white-space characters
8311 tape.no_valid_file = TRUE;
8313 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8321 if (tape.no_valid_file)
8324 tape.length_frames = GetTapeLengthFrames();
8325 tape.length_seconds = GetTapeLengthSeconds();
8328 void LoadTapeFromFilename(char *filename)
8330 char cookie[MAX_LINE_LEN];
8331 char chunk_name[CHUNK_ID_LEN + 1];
8335 // always start with reliable default values
8336 setTapeInfoToDefaults();
8338 if (strSuffix(filename, ".sln"))
8340 LoadTape_SokobanSolution(filename);
8345 if (!(file = openFile(filename, MODE_READ)))
8347 tape.no_valid_file = TRUE;
8352 getFileChunkBE(file, chunk_name, NULL);
8353 if (strEqual(chunk_name, "RND1"))
8355 getFile32BitBE(file); // not used
8357 getFileChunkBE(file, chunk_name, NULL);
8358 if (!strEqual(chunk_name, "TAPE"))
8360 tape.no_valid_file = TRUE;
8362 Warn("unknown format of tape file '%s'", filename);
8369 else // check for pre-2.0 file format with cookie string
8371 strcpy(cookie, chunk_name);
8372 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8374 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8375 cookie[strlen(cookie) - 1] = '\0';
8377 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8379 tape.no_valid_file = TRUE;
8381 Warn("unknown format of tape file '%s'", filename);
8388 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8390 tape.no_valid_file = TRUE;
8392 Warn("unsupported version of tape file '%s'", filename);
8399 // pre-2.0 tape files have no game version, so use file version here
8400 tape.game_version = tape.file_version;
8403 if (tape.file_version < FILE_VERSION_1_2)
8405 // tape files from versions before 1.2.0 without chunk structure
8406 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8407 LoadTape_BODY(file, 2 * tape.length, &tape);
8415 int (*loader)(File *, int, struct TapeInfo *);
8419 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8420 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8421 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8422 { "INFO", -1, LoadTape_INFO },
8423 { "BODY", -1, LoadTape_BODY },
8427 while (getFileChunkBE(file, chunk_name, &chunk_size))
8431 while (chunk_info[i].name != NULL &&
8432 !strEqual(chunk_name, chunk_info[i].name))
8435 if (chunk_info[i].name == NULL)
8437 Warn("unknown chunk '%s' in tape file '%s'",
8438 chunk_name, filename);
8440 ReadUnusedBytesFromFile(file, chunk_size);
8442 else if (chunk_info[i].size != -1 &&
8443 chunk_info[i].size != chunk_size)
8445 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8446 chunk_size, chunk_name, filename);
8448 ReadUnusedBytesFromFile(file, chunk_size);
8452 // call function to load this tape chunk
8453 int chunk_size_expected =
8454 (chunk_info[i].loader)(file, chunk_size, &tape);
8456 // the size of some chunks cannot be checked before reading other
8457 // chunks first (like "HEAD" and "BODY") that contain some header
8458 // information, so check them here
8459 if (chunk_size_expected != chunk_size)
8461 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8462 chunk_size, chunk_name, filename);
8470 tape.length_frames = GetTapeLengthFrames();
8471 tape.length_seconds = GetTapeLengthSeconds();
8474 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8476 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8478 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8479 tape.engine_version);
8483 void LoadTape(int nr)
8485 char *filename = getTapeFilename(nr);
8487 LoadTapeFromFilename(filename);
8490 void LoadSolutionTape(int nr)
8492 char *filename = getSolutionTapeFilename(nr);
8494 LoadTapeFromFilename(filename);
8496 if (TAPE_IS_EMPTY(tape) &&
8497 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8498 level.native_sp_level->demo.is_available)
8499 CopyNativeTape_SP_to_RND(&level);
8502 void LoadScoreTape(char *score_tape_basename, int nr)
8504 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8506 LoadTapeFromFilename(filename);
8509 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8511 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8513 LoadTapeFromFilename(filename);
8516 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8518 // chunk required for team mode tapes with non-default screen size
8519 return (tape->num_participating_players > 1 &&
8520 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8521 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8524 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8526 putFileVersion(file, tape->file_version);
8527 putFileVersion(file, tape->game_version);
8530 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8533 byte store_participating_players = 0;
8535 // set bits for participating players for compact storage
8536 for (i = 0; i < MAX_PLAYERS; i++)
8537 if (tape->player_participates[i])
8538 store_participating_players |= (1 << i);
8540 putFile32BitBE(file, tape->random_seed);
8541 putFile32BitBE(file, tape->date);
8542 putFile32BitBE(file, tape->length);
8544 putFile8Bit(file, store_participating_players);
8546 putFile8Bit(file, getTapeActionValue(tape));
8548 putFile8Bit(file, tape->property_bits);
8549 putFile8Bit(file, tape->solved);
8551 putFileVersion(file, tape->engine_version);
8554 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8556 putFile8Bit(file, tape->scr_fieldx);
8557 putFile8Bit(file, tape->scr_fieldy);
8560 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8562 int level_identifier_size = strlen(tape->level_identifier) + 1;
8565 putFile16BitBE(file, level_identifier_size);
8567 for (i = 0; i < level_identifier_size; i++)
8568 putFile8Bit(file, tape->level_identifier[i]);
8570 putFile16BitBE(file, tape->level_nr);
8573 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8577 for (i = 0; i < tape->length; i++)
8579 if (tape->use_key_actions)
8581 for (j = 0; j < MAX_PLAYERS; j++)
8582 if (tape->player_participates[j])
8583 putFile8Bit(file, tape->pos[i].action[j]);
8586 if (tape->use_mouse_actions)
8588 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8589 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8590 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8593 putFile8Bit(file, tape->pos[i].delay);
8597 void SaveTapeToFilename(char *filename)
8601 int info_chunk_size;
8602 int body_chunk_size;
8604 if (!(file = fopen(filename, MODE_WRITE)))
8606 Warn("cannot save level recording file '%s'", filename);
8611 tape_pos_size = getTapePosSize(&tape);
8613 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8614 body_chunk_size = tape_pos_size * tape.length;
8616 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8617 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8619 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8620 SaveTape_VERS(file, &tape);
8622 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8623 SaveTape_HEAD(file, &tape);
8625 if (checkSaveTape_SCRN(&tape))
8627 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8628 SaveTape_SCRN(file, &tape);
8631 putFileChunkBE(file, "INFO", info_chunk_size);
8632 SaveTape_INFO(file, &tape);
8634 putFileChunkBE(file, "BODY", body_chunk_size);
8635 SaveTape_BODY(file, &tape);
8639 SetFilePermissions(filename, PERMS_PRIVATE);
8642 static void SaveTapeExt(char *filename)
8646 tape.file_version = FILE_VERSION_ACTUAL;
8647 tape.game_version = GAME_VERSION_ACTUAL;
8649 tape.num_participating_players = 0;
8651 // count number of participating players
8652 for (i = 0; i < MAX_PLAYERS; i++)
8653 if (tape.player_participates[i])
8654 tape.num_participating_players++;
8656 SaveTapeToFilename(filename);
8658 tape.changed = FALSE;
8661 void SaveTape(int nr)
8663 char *filename = getTapeFilename(nr);
8665 InitTapeDirectory(leveldir_current->subdir);
8667 SaveTapeExt(filename);
8670 void SaveScoreTape(int nr)
8672 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8674 // used instead of "leveldir_current->subdir" (for network games)
8675 InitScoreTapeDirectory(levelset.identifier, nr);
8677 SaveTapeExt(filename);
8680 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8681 unsigned int req_state_added)
8683 char *filename = getTapeFilename(nr);
8684 boolean new_tape = !fileExists(filename);
8685 boolean tape_saved = FALSE;
8687 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8692 Request(msg_saved, REQ_CONFIRM | req_state_added);
8700 boolean SaveTapeChecked(int nr)
8702 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8705 boolean SaveTapeChecked_LevelSolved(int nr)
8707 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8708 "Level solved! Tape saved!", REQ_STAY_OPEN);
8711 void DumpTape(struct TapeInfo *tape)
8713 int tape_frame_counter;
8716 if (tape->no_valid_file)
8718 Warn("cannot dump -- no valid tape file found");
8725 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8726 tape->level_nr, tape->file_version, tape->game_version);
8727 Print(" (effective engine version %08d)\n",
8728 tape->engine_version);
8729 Print("Level series identifier: '%s'\n", tape->level_identifier);
8731 Print("Solution tape: %s\n",
8732 tape->solved ? "yes" :
8733 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8735 Print("Special tape properties: ");
8736 if (tape->property_bits == TAPE_PROPERTY_NONE)
8738 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8739 Print("[em_random_bug]");
8740 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8741 Print("[game_speed]");
8742 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8744 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8745 Print("[single_step]");
8746 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8747 Print("[snapshot]");
8748 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8749 Print("[replayed]");
8750 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8751 Print("[tas_keys]");
8752 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8753 Print("[small_graphics]");
8756 int year2 = tape->date / 10000;
8757 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8758 int month_index_raw = (tape->date / 100) % 100;
8759 int month_index = month_index_raw % 12; // prevent invalid index
8760 int month = month_index + 1;
8761 int day = tape->date % 100;
8763 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8767 tape_frame_counter = 0;
8769 for (i = 0; i < tape->length; i++)
8771 if (i >= MAX_TAPE_LEN)
8776 for (j = 0; j < MAX_PLAYERS; j++)
8778 if (tape->player_participates[j])
8780 int action = tape->pos[i].action[j];
8782 Print("%d:%02x ", j, action);
8783 Print("[%c%c%c%c|%c%c] - ",
8784 (action & JOY_LEFT ? '<' : ' '),
8785 (action & JOY_RIGHT ? '>' : ' '),
8786 (action & JOY_UP ? '^' : ' '),
8787 (action & JOY_DOWN ? 'v' : ' '),
8788 (action & JOY_BUTTON_1 ? '1' : ' '),
8789 (action & JOY_BUTTON_2 ? '2' : ' '));
8793 Print("(%03d) ", tape->pos[i].delay);
8794 Print("[%05d]\n", tape_frame_counter);
8796 tape_frame_counter += tape->pos[i].delay;
8802 void DumpTapes(void)
8804 static LevelDirTree *dumptape_leveldir = NULL;
8806 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8807 global.dumptape_leveldir);
8809 if (dumptape_leveldir == NULL)
8810 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8812 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8813 global.dumptape_level_nr > dumptape_leveldir->last_level)
8814 Fail("no such level number: %d", global.dumptape_level_nr);
8816 leveldir_current = dumptape_leveldir;
8818 if (options.mytapes)
8819 LoadTape(global.dumptape_level_nr);
8821 LoadSolutionTape(global.dumptape_level_nr);
8829 // ============================================================================
8830 // score file functions
8831 // ============================================================================
8833 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8837 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8839 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8840 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8841 scores->entry[i].score = 0;
8842 scores->entry[i].time = 0;
8844 scores->entry[i].id = -1;
8845 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8846 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8847 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8848 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8849 strcpy(scores->entry[i].country_code, "??");
8852 scores->num_entries = 0;
8853 scores->last_added = -1;
8854 scores->last_added_local = -1;
8856 scores->updated = FALSE;
8857 scores->uploaded = FALSE;
8858 scores->tape_downloaded = FALSE;
8859 scores->force_last_added = FALSE;
8861 // The following values are intentionally not reset here:
8865 // - continue_playing
8866 // - continue_on_return
8869 static void setScoreInfoToDefaults(void)
8871 setScoreInfoToDefaultsExt(&scores);
8874 static void setServerScoreInfoToDefaults(void)
8876 setScoreInfoToDefaultsExt(&server_scores);
8879 static void LoadScore_OLD(int nr)
8882 char *filename = getScoreFilename(nr);
8883 char cookie[MAX_LINE_LEN];
8884 char line[MAX_LINE_LEN];
8888 if (!(file = fopen(filename, MODE_READ)))
8891 // check file identifier
8892 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8894 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8895 cookie[strlen(cookie) - 1] = '\0';
8897 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8899 Warn("unknown format of score file '%s'", filename);
8906 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8908 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8909 Warn("fscanf() failed; %s", strerror(errno));
8911 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8914 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8915 line[strlen(line) - 1] = '\0';
8917 for (line_ptr = line; *line_ptr; line_ptr++)
8919 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8921 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8922 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8931 static void ConvertScore_OLD(void)
8933 // only convert score to time for levels that rate playing time over score
8934 if (!level.rate_time_over_score)
8937 // convert old score to playing time for score-less levels (like Supaplex)
8938 int time_final_max = 999;
8941 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8943 int score = scores.entry[i].score;
8945 if (score > 0 && score < time_final_max)
8946 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8950 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8952 scores->file_version = getFileVersion(file);
8953 scores->game_version = getFileVersion(file);
8958 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8960 char *level_identifier = NULL;
8961 int level_identifier_size;
8964 level_identifier_size = getFile16BitBE(file);
8966 level_identifier = checked_malloc(level_identifier_size);
8968 for (i = 0; i < level_identifier_size; i++)
8969 level_identifier[i] = getFile8Bit(file);
8971 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8972 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8974 checked_free(level_identifier);
8976 scores->level_nr = getFile16BitBE(file);
8977 scores->num_entries = getFile16BitBE(file);
8979 chunk_size = 2 + level_identifier_size + 2 + 2;
8984 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8988 for (i = 0; i < scores->num_entries; i++)
8990 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8991 scores->entry[i].name[j] = getFile8Bit(file);
8993 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8996 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9001 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9005 for (i = 0; i < scores->num_entries; i++)
9006 scores->entry[i].score = getFile16BitBE(file);
9008 chunk_size = scores->num_entries * 2;
9013 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9017 for (i = 0; i < scores->num_entries; i++)
9018 scores->entry[i].score = getFile32BitBE(file);
9020 chunk_size = scores->num_entries * 4;
9025 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9029 for (i = 0; i < scores->num_entries; i++)
9030 scores->entry[i].time = getFile32BitBE(file);
9032 chunk_size = scores->num_entries * 4;
9037 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9041 for (i = 0; i < scores->num_entries; i++)
9043 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9044 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9046 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9049 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9054 void LoadScore(int nr)
9056 char *filename = getScoreFilename(nr);
9057 char cookie[MAX_LINE_LEN];
9058 char chunk_name[CHUNK_ID_LEN + 1];
9060 boolean old_score_file_format = FALSE;
9063 // always start with reliable default values
9064 setScoreInfoToDefaults();
9066 if (!(file = openFile(filename, MODE_READ)))
9069 getFileChunkBE(file, chunk_name, NULL);
9070 if (strEqual(chunk_name, "RND1"))
9072 getFile32BitBE(file); // not used
9074 getFileChunkBE(file, chunk_name, NULL);
9075 if (!strEqual(chunk_name, "SCOR"))
9077 Warn("unknown format of score file '%s'", filename);
9084 else // check for old file format with cookie string
9086 strcpy(cookie, chunk_name);
9087 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9089 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9090 cookie[strlen(cookie) - 1] = '\0';
9092 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9094 Warn("unknown format of score file '%s'", filename);
9101 old_score_file_format = TRUE;
9104 if (old_score_file_format)
9106 // score files from versions before 4.2.4.0 without chunk structure
9109 // convert score to time, if possible (mainly for Supaplex levels)
9118 int (*loader)(File *, int, struct ScoreInfo *);
9122 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9123 { "INFO", -1, LoadScore_INFO },
9124 { "NAME", -1, LoadScore_NAME },
9125 { "SCOR", -1, LoadScore_SCOR },
9126 { "SC4R", -1, LoadScore_SC4R },
9127 { "TIME", -1, LoadScore_TIME },
9128 { "TAPE", -1, LoadScore_TAPE },
9133 while (getFileChunkBE(file, chunk_name, &chunk_size))
9137 while (chunk_info[i].name != NULL &&
9138 !strEqual(chunk_name, chunk_info[i].name))
9141 if (chunk_info[i].name == NULL)
9143 Warn("unknown chunk '%s' in score file '%s'",
9144 chunk_name, filename);
9146 ReadUnusedBytesFromFile(file, chunk_size);
9148 else if (chunk_info[i].size != -1 &&
9149 chunk_info[i].size != chunk_size)
9151 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9152 chunk_size, chunk_name, filename);
9154 ReadUnusedBytesFromFile(file, chunk_size);
9158 // call function to load this score chunk
9159 int chunk_size_expected =
9160 (chunk_info[i].loader)(file, chunk_size, &scores);
9162 // the size of some chunks cannot be checked before reading other
9163 // chunks first (like "HEAD" and "BODY") that contain some header
9164 // information, so check them here
9165 if (chunk_size_expected != chunk_size)
9167 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9168 chunk_size, chunk_name, filename);
9177 #if ENABLE_HISTORIC_CHUNKS
9178 void SaveScore_OLD(int nr)
9181 char *filename = getScoreFilename(nr);
9184 // used instead of "leveldir_current->subdir" (for network games)
9185 InitScoreDirectory(levelset.identifier);
9187 if (!(file = fopen(filename, MODE_WRITE)))
9189 Warn("cannot save score for level %d", nr);
9194 fprintf(file, "%s\n\n", SCORE_COOKIE);
9196 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9197 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9201 SetFilePermissions(filename, PERMS_PRIVATE);
9205 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9207 putFileVersion(file, scores->file_version);
9208 putFileVersion(file, scores->game_version);
9211 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9213 int level_identifier_size = strlen(scores->level_identifier) + 1;
9216 putFile16BitBE(file, level_identifier_size);
9218 for (i = 0; i < level_identifier_size; i++)
9219 putFile8Bit(file, scores->level_identifier[i]);
9221 putFile16BitBE(file, scores->level_nr);
9222 putFile16BitBE(file, scores->num_entries);
9225 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9229 for (i = 0; i < scores->num_entries; i++)
9231 int name_size = strlen(scores->entry[i].name);
9233 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9234 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9238 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9242 for (i = 0; i < scores->num_entries; i++)
9243 putFile16BitBE(file, scores->entry[i].score);
9246 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9250 for (i = 0; i < scores->num_entries; i++)
9251 putFile32BitBE(file, scores->entry[i].score);
9254 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9258 for (i = 0; i < scores->num_entries; i++)
9259 putFile32BitBE(file, scores->entry[i].time);
9262 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9266 for (i = 0; i < scores->num_entries; i++)
9268 int size = strlen(scores->entry[i].tape_basename);
9270 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9271 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9275 static void SaveScoreToFilename(char *filename)
9278 int info_chunk_size;
9279 int name_chunk_size;
9280 int scor_chunk_size;
9281 int sc4r_chunk_size;
9282 int time_chunk_size;
9283 int tape_chunk_size;
9284 boolean has_large_score_values;
9287 if (!(file = fopen(filename, MODE_WRITE)))
9289 Warn("cannot save score file '%s'", filename);
9294 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9295 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9296 scor_chunk_size = scores.num_entries * 2;
9297 sc4r_chunk_size = scores.num_entries * 4;
9298 time_chunk_size = scores.num_entries * 4;
9299 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9301 has_large_score_values = FALSE;
9302 for (i = 0; i < scores.num_entries; i++)
9303 if (scores.entry[i].score > 0xffff)
9304 has_large_score_values = TRUE;
9306 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9307 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9309 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9310 SaveScore_VERS(file, &scores);
9312 putFileChunkBE(file, "INFO", info_chunk_size);
9313 SaveScore_INFO(file, &scores);
9315 putFileChunkBE(file, "NAME", name_chunk_size);
9316 SaveScore_NAME(file, &scores);
9318 if (has_large_score_values)
9320 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9321 SaveScore_SC4R(file, &scores);
9325 putFileChunkBE(file, "SCOR", scor_chunk_size);
9326 SaveScore_SCOR(file, &scores);
9329 putFileChunkBE(file, "TIME", time_chunk_size);
9330 SaveScore_TIME(file, &scores);
9332 putFileChunkBE(file, "TAPE", tape_chunk_size);
9333 SaveScore_TAPE(file, &scores);
9337 SetFilePermissions(filename, PERMS_PRIVATE);
9340 void SaveScore(int nr)
9342 char *filename = getScoreFilename(nr);
9345 // used instead of "leveldir_current->subdir" (for network games)
9346 InitScoreDirectory(levelset.identifier);
9348 scores.file_version = FILE_VERSION_ACTUAL;
9349 scores.game_version = GAME_VERSION_ACTUAL;
9351 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9352 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9353 scores.level_nr = level_nr;
9355 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9356 if (scores.entry[i].score == 0 &&
9357 scores.entry[i].time == 0 &&
9358 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9361 scores.num_entries = i;
9363 if (scores.num_entries == 0)
9366 SaveScoreToFilename(filename);
9369 static void LoadServerScoreFromCache(int nr)
9371 struct ScoreEntry score_entry;
9380 { &score_entry.score, FALSE, 0 },
9381 { &score_entry.time, FALSE, 0 },
9382 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9383 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9384 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9385 { &score_entry.id, FALSE, 0 },
9386 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9387 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9388 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9389 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9393 char *filename = getScoreCacheFilename(nr);
9394 SetupFileHash *score_hash = loadSetupFileHash(filename);
9397 server_scores.num_entries = 0;
9399 if (score_hash == NULL)
9402 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9404 score_entry = server_scores.entry[i];
9406 for (j = 0; score_mapping[j].value != NULL; j++)
9410 sprintf(token, "%02d.%d", i, j);
9412 char *value = getHashEntry(score_hash, token);
9417 if (score_mapping[j].is_string)
9419 char *score_value = (char *)score_mapping[j].value;
9420 int value_size = score_mapping[j].string_size;
9422 strncpy(score_value, value, value_size);
9423 score_value[value_size] = '\0';
9427 int *score_value = (int *)score_mapping[j].value;
9429 *score_value = atoi(value);
9432 server_scores.num_entries = i + 1;
9435 server_scores.entry[i] = score_entry;
9438 freeSetupFileHash(score_hash);
9441 void LoadServerScore(int nr, boolean download_score)
9443 if (!setup.use_api_server)
9446 // always start with reliable default values
9447 setServerScoreInfoToDefaults();
9449 // 1st step: load server scores from cache file (which may not exist)
9450 // (this should prevent reading it while the thread is writing to it)
9451 LoadServerScoreFromCache(nr);
9453 if (download_score && runtime.use_api_server)
9455 // 2nd step: download server scores from score server to cache file
9456 // (as thread, as it might time out if the server is not reachable)
9457 ApiGetScoreAsThread(nr);
9461 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9463 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9465 // if score tape not uploaded, ask for uploading missing tapes later
9466 if (!setup.has_remaining_tapes)
9467 setup.ask_for_remaining_tapes = TRUE;
9469 setup.provide_uploading_tapes = TRUE;
9470 setup.has_remaining_tapes = TRUE;
9472 SaveSetup_ServerSetup();
9475 void SaveServerScore(int nr, boolean tape_saved)
9477 if (!runtime.use_api_server)
9479 PrepareScoreTapesForUpload(leveldir_current->subdir);
9484 ApiAddScoreAsThread(nr, tape_saved, NULL);
9487 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9488 char *score_tape_filename)
9490 if (!runtime.use_api_server)
9493 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9496 void LoadLocalAndServerScore(int nr, boolean download_score)
9498 int last_added_local = scores.last_added_local;
9499 boolean force_last_added = scores.force_last_added;
9501 // needed if only showing server scores
9502 setScoreInfoToDefaults();
9504 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9507 // restore last added local score entry (before merging server scores)
9508 scores.last_added = scores.last_added_local = last_added_local;
9510 if (setup.use_api_server &&
9511 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9513 // load server scores from cache file and trigger update from server
9514 LoadServerScore(nr, download_score);
9516 // merge local scores with scores from server
9520 if (force_last_added)
9521 scores.force_last_added = force_last_added;
9525 // ============================================================================
9526 // setup file functions
9527 // ============================================================================
9529 #define TOKEN_STR_PLAYER_PREFIX "player_"
9532 static struct TokenInfo global_setup_tokens[] =
9536 &setup.player_name, "player_name"
9540 &setup.multiple_users, "multiple_users"
9544 &setup.sound, "sound"
9548 &setup.sound_loops, "repeating_sound_loops"
9552 &setup.sound_music, "background_music"
9556 &setup.sound_simple, "simple_sound_effects"
9560 &setup.toons, "toons"
9564 &setup.global_animations, "global_animations"
9568 &setup.scroll_delay, "scroll_delay"
9572 &setup.forced_scroll_delay, "forced_scroll_delay"
9576 &setup.scroll_delay_value, "scroll_delay_value"
9580 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9584 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9588 &setup.fade_screens, "fade_screens"
9592 &setup.autorecord, "automatic_tape_recording"
9596 &setup.autorecord_after_replay, "autorecord_after_replay"
9600 &setup.auto_pause_on_start, "auto_pause_on_start"
9604 &setup.show_titlescreen, "show_titlescreen"
9608 &setup.quick_doors, "quick_doors"
9612 &setup.team_mode, "team_mode"
9616 &setup.handicap, "handicap"
9620 &setup.skip_levels, "skip_levels"
9624 &setup.increment_levels, "increment_levels"
9628 &setup.auto_play_next_level, "auto_play_next_level"
9632 &setup.count_score_after_game, "count_score_after_game"
9636 &setup.show_scores_after_game, "show_scores_after_game"
9640 &setup.time_limit, "time_limit"
9644 &setup.fullscreen, "fullscreen"
9648 &setup.window_scaling_percent, "window_scaling_percent"
9652 &setup.window_scaling_quality, "window_scaling_quality"
9656 &setup.screen_rendering_mode, "screen_rendering_mode"
9660 &setup.vsync_mode, "vsync_mode"
9664 &setup.ask_on_escape, "ask_on_escape"
9668 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9672 &setup.ask_on_game_over, "ask_on_game_over"
9676 &setup.ask_on_quit_game, "ask_on_quit_game"
9680 &setup.ask_on_quit_program, "ask_on_quit_program"
9684 &setup.quick_switch, "quick_player_switch"
9688 &setup.input_on_focus, "input_on_focus"
9692 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9696 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9700 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9704 &setup.game_speed_extended, "game_speed_extended"
9708 &setup.game_frame_delay, "game_frame_delay"
9712 &setup.sp_show_border_elements, "sp_show_border_elements"
9716 &setup.small_game_graphics, "small_game_graphics"
9720 &setup.show_load_save_buttons, "show_load_save_buttons"
9724 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9728 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9732 &setup.graphics_set, "graphics_set"
9736 &setup.sounds_set, "sounds_set"
9740 &setup.music_set, "music_set"
9744 &setup.override_level_graphics, "override_level_graphics"
9748 &setup.override_level_sounds, "override_level_sounds"
9752 &setup.override_level_music, "override_level_music"
9756 &setup.volume_simple, "volume_simple"
9760 &setup.volume_loops, "volume_loops"
9764 &setup.volume_music, "volume_music"
9768 &setup.network_mode, "network_mode"
9772 &setup.network_player_nr, "network_player"
9776 &setup.network_server_hostname, "network_server_hostname"
9780 &setup.touch.control_type, "touch.control_type"
9784 &setup.touch.move_distance, "touch.move_distance"
9788 &setup.touch.drop_distance, "touch.drop_distance"
9792 &setup.touch.transparency, "touch.transparency"
9796 &setup.touch.draw_outlined, "touch.draw_outlined"
9800 &setup.touch.draw_pressed, "touch.draw_pressed"
9804 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9808 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9812 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9816 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9820 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9824 static struct TokenInfo auto_setup_tokens[] =
9828 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9832 static struct TokenInfo server_setup_tokens[] =
9836 &setup.player_uuid, "player_uuid"
9840 &setup.player_version, "player_version"
9844 &setup.use_api_server, TEST_PREFIX "use_api_server"
9848 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9852 &setup.api_server_password, TEST_PREFIX "api_server_password"
9856 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9860 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9864 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9868 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9872 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9876 static struct TokenInfo editor_setup_tokens[] =
9880 &setup.editor.el_classic, "editor.el_classic"
9884 &setup.editor.el_custom, "editor.el_custom"
9888 &setup.editor.el_user_defined, "editor.el_user_defined"
9892 &setup.editor.el_dynamic, "editor.el_dynamic"
9896 &setup.editor.el_headlines, "editor.el_headlines"
9900 &setup.editor.show_element_token, "editor.show_element_token"
9904 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9908 static struct TokenInfo editor_cascade_setup_tokens[] =
9912 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9916 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
9920 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9924 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9928 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9932 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9936 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9940 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9944 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9948 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9952 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9956 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9960 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9964 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9968 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9972 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9976 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9980 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9984 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9988 static struct TokenInfo shortcut_setup_tokens[] =
9992 &setup.shortcut.save_game, "shortcut.save_game"
9996 &setup.shortcut.load_game, "shortcut.load_game"
10000 &setup.shortcut.restart_game, "shortcut.restart_game"
10004 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10008 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10012 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10016 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10020 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10024 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10028 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10032 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10036 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10040 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10044 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10048 &setup.shortcut.tape_record, "shortcut.tape_record"
10052 &setup.shortcut.tape_play, "shortcut.tape_play"
10056 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10060 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10064 &setup.shortcut.sound_music, "shortcut.sound_music"
10068 &setup.shortcut.snap_left, "shortcut.snap_left"
10072 &setup.shortcut.snap_right, "shortcut.snap_right"
10076 &setup.shortcut.snap_up, "shortcut.snap_up"
10080 &setup.shortcut.snap_down, "shortcut.snap_down"
10084 static struct SetupInputInfo setup_input;
10085 static struct TokenInfo player_setup_tokens[] =
10089 &setup_input.use_joystick, ".use_joystick"
10093 &setup_input.joy.device_name, ".joy.device_name"
10097 &setup_input.joy.xleft, ".joy.xleft"
10101 &setup_input.joy.xmiddle, ".joy.xmiddle"
10105 &setup_input.joy.xright, ".joy.xright"
10109 &setup_input.joy.yupper, ".joy.yupper"
10113 &setup_input.joy.ymiddle, ".joy.ymiddle"
10117 &setup_input.joy.ylower, ".joy.ylower"
10121 &setup_input.joy.snap, ".joy.snap_field"
10125 &setup_input.joy.drop, ".joy.place_bomb"
10129 &setup_input.key.left, ".key.move_left"
10133 &setup_input.key.right, ".key.move_right"
10137 &setup_input.key.up, ".key.move_up"
10141 &setup_input.key.down, ".key.move_down"
10145 &setup_input.key.snap, ".key.snap_field"
10149 &setup_input.key.drop, ".key.place_bomb"
10153 static struct TokenInfo system_setup_tokens[] =
10157 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10161 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10165 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10169 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10173 static struct TokenInfo internal_setup_tokens[] =
10177 &setup.internal.program_title, "program_title"
10181 &setup.internal.program_version, "program_version"
10185 &setup.internal.program_author, "program_author"
10189 &setup.internal.program_email, "program_email"
10193 &setup.internal.program_website, "program_website"
10197 &setup.internal.program_copyright, "program_copyright"
10201 &setup.internal.program_company, "program_company"
10205 &setup.internal.program_icon_file, "program_icon_file"
10209 &setup.internal.default_graphics_set, "default_graphics_set"
10213 &setup.internal.default_sounds_set, "default_sounds_set"
10217 &setup.internal.default_music_set, "default_music_set"
10221 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10225 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10229 &setup.internal.fallback_music_file, "fallback_music_file"
10233 &setup.internal.default_level_series, "default_level_series"
10237 &setup.internal.default_window_width, "default_window_width"
10241 &setup.internal.default_window_height, "default_window_height"
10245 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10249 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10253 &setup.internal.create_user_levelset, "create_user_levelset"
10257 &setup.internal.info_screens_from_main, "info_screens_from_main"
10261 &setup.internal.menu_game, "menu_game"
10265 &setup.internal.menu_engines, "menu_engines"
10269 &setup.internal.menu_editor, "menu_editor"
10273 &setup.internal.menu_graphics, "menu_graphics"
10277 &setup.internal.menu_sound, "menu_sound"
10281 &setup.internal.menu_artwork, "menu_artwork"
10285 &setup.internal.menu_input, "menu_input"
10289 &setup.internal.menu_touch, "menu_touch"
10293 &setup.internal.menu_shortcuts, "menu_shortcuts"
10297 &setup.internal.menu_exit, "menu_exit"
10301 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10305 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10309 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10313 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10317 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10321 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10325 &setup.internal.info_title, "info_title"
10329 &setup.internal.info_elements, "info_elements"
10333 &setup.internal.info_music, "info_music"
10337 &setup.internal.info_credits, "info_credits"
10341 &setup.internal.info_program, "info_program"
10345 &setup.internal.info_version, "info_version"
10349 &setup.internal.info_levelset, "info_levelset"
10353 &setup.internal.info_exit, "info_exit"
10357 static struct TokenInfo debug_setup_tokens[] =
10361 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10365 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10369 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10373 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10377 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10381 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10385 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10389 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10393 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10397 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10401 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10405 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10409 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10413 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10417 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10421 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10425 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10429 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10433 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10437 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10441 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10444 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10448 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10452 &setup.debug.xsn_mode, "debug.xsn_mode"
10456 &setup.debug.xsn_percent, "debug.xsn_percent"
10460 static struct TokenInfo options_setup_tokens[] =
10464 &setup.options.verbose, "options.verbose"
10468 &setup.options.debug, "options.debug"
10472 &setup.options.debug_mode, "options.debug_mode"
10476 static void setSetupInfoToDefaults(struct SetupInfo *si)
10480 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10482 si->multiple_users = TRUE;
10485 si->sound_loops = TRUE;
10486 si->sound_music = TRUE;
10487 si->sound_simple = TRUE;
10489 si->global_animations = TRUE;
10490 si->scroll_delay = TRUE;
10491 si->forced_scroll_delay = FALSE;
10492 si->scroll_delay_value = STD_SCROLL_DELAY;
10493 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10494 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10495 si->fade_screens = TRUE;
10496 si->autorecord = TRUE;
10497 si->autorecord_after_replay = TRUE;
10498 si->auto_pause_on_start = FALSE;
10499 si->show_titlescreen = TRUE;
10500 si->quick_doors = FALSE;
10501 si->team_mode = FALSE;
10502 si->handicap = TRUE;
10503 si->skip_levels = TRUE;
10504 si->increment_levels = TRUE;
10505 si->auto_play_next_level = TRUE;
10506 si->count_score_after_game = TRUE;
10507 si->show_scores_after_game = TRUE;
10508 si->time_limit = TRUE;
10509 si->fullscreen = FALSE;
10510 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10511 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10512 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10513 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10514 si->ask_on_escape = TRUE;
10515 si->ask_on_escape_editor = TRUE;
10516 si->ask_on_game_over = TRUE;
10517 si->ask_on_quit_game = TRUE;
10518 si->ask_on_quit_program = TRUE;
10519 si->quick_switch = FALSE;
10520 si->input_on_focus = FALSE;
10521 si->prefer_aga_graphics = TRUE;
10522 si->prefer_lowpass_sounds = FALSE;
10523 si->prefer_extra_panel_items = TRUE;
10524 si->game_speed_extended = FALSE;
10525 si->game_frame_delay = GAME_FRAME_DELAY;
10526 si->sp_show_border_elements = FALSE;
10527 si->small_game_graphics = FALSE;
10528 si->show_load_save_buttons = FALSE;
10529 si->show_undo_redo_buttons = FALSE;
10530 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10532 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10533 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10534 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10536 si->override_level_graphics = FALSE;
10537 si->override_level_sounds = FALSE;
10538 si->override_level_music = FALSE;
10540 si->volume_simple = 100; // percent
10541 si->volume_loops = 100; // percent
10542 si->volume_music = 100; // percent
10544 si->network_mode = FALSE;
10545 si->network_player_nr = 0; // first player
10546 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10548 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10549 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10550 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10551 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10552 si->touch.draw_outlined = TRUE;
10553 si->touch.draw_pressed = TRUE;
10555 for (i = 0; i < 2; i++)
10557 char *default_grid_button[6][2] =
10563 { "111222", " vv " },
10564 { "111222", " vv " }
10566 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10567 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10568 int min_xsize = MIN(6, grid_xsize);
10569 int min_ysize = MIN(6, grid_ysize);
10570 int startx = grid_xsize - min_xsize;
10571 int starty = grid_ysize - min_ysize;
10574 // virtual buttons grid can only be set to defaults if video is initialized
10575 // (this will be repeated if virtual buttons are not loaded from setup file)
10576 if (video.initialized)
10578 si->touch.grid_xsize[i] = grid_xsize;
10579 si->touch.grid_ysize[i] = grid_ysize;
10583 si->touch.grid_xsize[i] = -1;
10584 si->touch.grid_ysize[i] = -1;
10587 for (x = 0; x < MAX_GRID_XSIZE; x++)
10588 for (y = 0; y < MAX_GRID_YSIZE; y++)
10589 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10591 for (x = 0; x < min_xsize; x++)
10592 for (y = 0; y < min_ysize; y++)
10593 si->touch.grid_button[i][x][starty + y] =
10594 default_grid_button[y][0][x];
10596 for (x = 0; x < min_xsize; x++)
10597 for (y = 0; y < min_ysize; y++)
10598 si->touch.grid_button[i][startx + x][starty + y] =
10599 default_grid_button[y][1][x];
10602 si->touch.grid_initialized = video.initialized;
10604 si->touch.overlay_buttons = FALSE;
10606 si->editor.el_boulderdash = TRUE;
10607 si->editor.el_boulderdash_native = TRUE;
10608 si->editor.el_emerald_mine = TRUE;
10609 si->editor.el_emerald_mine_club = TRUE;
10610 si->editor.el_more = TRUE;
10611 si->editor.el_sokoban = TRUE;
10612 si->editor.el_supaplex = TRUE;
10613 si->editor.el_diamond_caves = TRUE;
10614 si->editor.el_dx_boulderdash = TRUE;
10616 si->editor.el_mirror_magic = TRUE;
10617 si->editor.el_deflektor = TRUE;
10619 si->editor.el_chars = TRUE;
10620 si->editor.el_steel_chars = TRUE;
10622 si->editor.el_classic = TRUE;
10623 si->editor.el_custom = TRUE;
10625 si->editor.el_user_defined = FALSE;
10626 si->editor.el_dynamic = TRUE;
10628 si->editor.el_headlines = TRUE;
10630 si->editor.show_element_token = FALSE;
10632 si->editor.show_read_only_warning = TRUE;
10634 si->editor.use_template_for_new_levels = TRUE;
10636 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10637 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10638 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10639 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10640 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10642 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10643 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10644 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10645 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10646 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10648 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10649 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10650 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10651 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10652 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10653 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10655 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10656 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10657 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10659 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10660 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10661 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10662 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10664 for (i = 0; i < MAX_PLAYERS; i++)
10666 si->input[i].use_joystick = FALSE;
10667 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10668 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10669 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10670 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10671 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10672 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10673 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10674 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10675 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10676 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10677 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10678 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10679 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10680 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10681 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10684 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10685 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10686 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10687 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10689 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10690 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10691 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10692 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10693 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10694 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10695 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10697 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10699 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10700 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10701 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10703 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10704 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10705 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10707 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10708 si->internal.choose_from_top_leveldir = FALSE;
10709 si->internal.show_scaling_in_title = TRUE;
10710 si->internal.create_user_levelset = TRUE;
10711 si->internal.info_screens_from_main = FALSE;
10713 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10714 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10716 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10717 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10718 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10719 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10720 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10721 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10722 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10723 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10724 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10725 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10727 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10728 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10729 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10730 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10731 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10732 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10733 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10734 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10735 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10736 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10738 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10739 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10741 si->debug.show_frames_per_second = FALSE;
10743 si->debug.xsn_mode = AUTO;
10744 si->debug.xsn_percent = 0;
10746 si->options.verbose = FALSE;
10747 si->options.debug = FALSE;
10748 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10750 #if defined(PLATFORM_ANDROID)
10751 si->fullscreen = TRUE;
10752 si->touch.overlay_buttons = TRUE;
10755 setHideSetupEntry(&setup.debug.xsn_mode);
10758 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10760 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10763 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10765 si->player_uuid = NULL; // (will be set later)
10766 si->player_version = 1; // (will be set later)
10768 si->use_api_server = TRUE;
10769 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10770 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10771 si->ask_for_uploading_tapes = TRUE;
10772 si->ask_for_remaining_tapes = FALSE;
10773 si->provide_uploading_tapes = TRUE;
10774 si->ask_for_using_api_server = TRUE;
10775 si->has_remaining_tapes = FALSE;
10778 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10780 si->editor_cascade.el_bd = TRUE;
10781 si->editor_cascade.el_bd_native = TRUE;
10782 si->editor_cascade.el_em = TRUE;
10783 si->editor_cascade.el_emc = TRUE;
10784 si->editor_cascade.el_rnd = TRUE;
10785 si->editor_cascade.el_sb = TRUE;
10786 si->editor_cascade.el_sp = TRUE;
10787 si->editor_cascade.el_dc = TRUE;
10788 si->editor_cascade.el_dx = TRUE;
10790 si->editor_cascade.el_mm = TRUE;
10791 si->editor_cascade.el_df = TRUE;
10793 si->editor_cascade.el_chars = FALSE;
10794 si->editor_cascade.el_steel_chars = FALSE;
10795 si->editor_cascade.el_ce = FALSE;
10796 si->editor_cascade.el_ge = FALSE;
10797 si->editor_cascade.el_es = FALSE;
10798 si->editor_cascade.el_ref = FALSE;
10799 si->editor_cascade.el_user = FALSE;
10800 si->editor_cascade.el_dynamic = FALSE;
10803 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10805 static char *getHideSetupToken(void *setup_value)
10807 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10809 if (setup_value != NULL)
10810 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10812 return hide_setup_token;
10815 void setHideSetupEntry(void *setup_value)
10817 char *hide_setup_token = getHideSetupToken(setup_value);
10819 if (hide_setup_hash == NULL)
10820 hide_setup_hash = newSetupFileHash();
10822 if (setup_value != NULL)
10823 setHashEntry(hide_setup_hash, hide_setup_token, "");
10826 void removeHideSetupEntry(void *setup_value)
10828 char *hide_setup_token = getHideSetupToken(setup_value);
10830 if (setup_value != NULL)
10831 removeHashEntry(hide_setup_hash, hide_setup_token);
10834 boolean hideSetupEntry(void *setup_value)
10836 char *hide_setup_token = getHideSetupToken(setup_value);
10838 return (setup_value != NULL &&
10839 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10842 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10843 struct TokenInfo *token_info,
10844 int token_nr, char *token_text)
10846 char *token_hide_text = getStringCat2(token_text, ".hide");
10847 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10849 // set the value of this setup option in the setup option structure
10850 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10852 // check if this setup option should be hidden in the setup menu
10853 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10854 setHideSetupEntry(token_info[token_nr].value);
10856 free(token_hide_text);
10859 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10860 struct TokenInfo *token_info,
10863 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10864 token_info[token_nr].text);
10867 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10871 if (!setup_file_hash)
10874 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10875 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10877 setup.touch.grid_initialized = TRUE;
10878 for (i = 0; i < 2; i++)
10880 int grid_xsize = setup.touch.grid_xsize[i];
10881 int grid_ysize = setup.touch.grid_ysize[i];
10884 // if virtual buttons are not loaded from setup file, repeat initializing
10885 // virtual buttons grid with default values later when video is initialized
10886 if (grid_xsize == -1 ||
10889 setup.touch.grid_initialized = FALSE;
10894 for (y = 0; y < grid_ysize; y++)
10896 char token_string[MAX_LINE_LEN];
10898 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10900 char *value_string = getHashEntry(setup_file_hash, token_string);
10902 if (value_string == NULL)
10905 for (x = 0; x < grid_xsize; x++)
10907 char c = value_string[x];
10909 setup.touch.grid_button[i][x][y] =
10910 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10915 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10916 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10918 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10919 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10921 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10925 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10927 setup_input = setup.input[pnr];
10928 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10930 char full_token[100];
10932 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10933 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10936 setup.input[pnr] = setup_input;
10939 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10940 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10942 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10943 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10945 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10946 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10948 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10949 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10951 setHideRelatedSetupEntries();
10954 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10958 if (!setup_file_hash)
10961 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10962 setSetupInfo(auto_setup_tokens, i,
10963 getHashEntry(setup_file_hash,
10964 auto_setup_tokens[i].text));
10967 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10971 if (!setup_file_hash)
10974 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10975 setSetupInfo(server_setup_tokens, i,
10976 getHashEntry(setup_file_hash,
10977 server_setup_tokens[i].text));
10980 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10984 if (!setup_file_hash)
10987 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10988 setSetupInfo(editor_cascade_setup_tokens, i,
10989 getHashEntry(setup_file_hash,
10990 editor_cascade_setup_tokens[i].text));
10993 void LoadUserNames(void)
10995 int last_user_nr = user.nr;
10998 if (global.user_names != NULL)
11000 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11001 checked_free(global.user_names[i]);
11003 checked_free(global.user_names);
11006 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11008 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11012 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11014 if (setup_file_hash)
11016 char *player_name = getHashEntry(setup_file_hash, "player_name");
11018 global.user_names[i] = getFixedUserName(player_name);
11020 freeSetupFileHash(setup_file_hash);
11023 if (global.user_names[i] == NULL)
11024 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11027 user.nr = last_user_nr;
11030 void LoadSetupFromFilename(char *filename)
11032 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11034 if (setup_file_hash)
11036 decodeSetupFileHash_Default(setup_file_hash);
11038 freeSetupFileHash(setup_file_hash);
11042 Debug("setup", "using default setup values");
11046 static void LoadSetup_SpecialPostProcessing(void)
11048 char *player_name_new;
11050 // needed to work around problems with fixed length strings
11051 player_name_new = getFixedUserName(setup.player_name);
11052 free(setup.player_name);
11053 setup.player_name = player_name_new;
11055 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11056 if (setup.scroll_delay == FALSE)
11058 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11059 setup.scroll_delay = TRUE; // now always "on"
11062 // make sure that scroll delay value stays inside valid range
11063 setup.scroll_delay_value =
11064 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11067 void LoadSetup_Default(void)
11071 // always start with reliable default values
11072 setSetupInfoToDefaults(&setup);
11074 // try to load setup values from default setup file
11075 filename = getDefaultSetupFilename();
11077 if (fileExists(filename))
11078 LoadSetupFromFilename(filename);
11080 // try to load setup values from platform setup file
11081 filename = getPlatformSetupFilename();
11083 if (fileExists(filename))
11084 LoadSetupFromFilename(filename);
11086 // try to load setup values from user setup file
11087 filename = getSetupFilename();
11089 LoadSetupFromFilename(filename);
11091 LoadSetup_SpecialPostProcessing();
11094 void LoadSetup_AutoSetup(void)
11096 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11097 SetupFileHash *setup_file_hash = NULL;
11099 // always start with reliable default values
11100 setSetupInfoToDefaults_AutoSetup(&setup);
11102 setup_file_hash = loadSetupFileHash(filename);
11104 if (setup_file_hash)
11106 decodeSetupFileHash_AutoSetup(setup_file_hash);
11108 freeSetupFileHash(setup_file_hash);
11114 void LoadSetup_ServerSetup(void)
11116 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11117 SetupFileHash *setup_file_hash = NULL;
11119 // always start with reliable default values
11120 setSetupInfoToDefaults_ServerSetup(&setup);
11122 setup_file_hash = loadSetupFileHash(filename);
11124 if (setup_file_hash)
11126 decodeSetupFileHash_ServerSetup(setup_file_hash);
11128 freeSetupFileHash(setup_file_hash);
11133 if (setup.player_uuid == NULL)
11135 // player UUID does not yet exist in setup file
11136 setup.player_uuid = getStringCopy(getUUID());
11137 setup.player_version = 2;
11139 SaveSetup_ServerSetup();
11143 void LoadSetup_EditorCascade(void)
11145 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11146 SetupFileHash *setup_file_hash = NULL;
11148 // always start with reliable default values
11149 setSetupInfoToDefaults_EditorCascade(&setup);
11151 setup_file_hash = loadSetupFileHash(filename);
11153 if (setup_file_hash)
11155 decodeSetupFileHash_EditorCascade(setup_file_hash);
11157 freeSetupFileHash(setup_file_hash);
11163 void LoadSetup(void)
11165 LoadSetup_Default();
11166 LoadSetup_AutoSetup();
11167 LoadSetup_ServerSetup();
11168 LoadSetup_EditorCascade();
11171 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11172 char *mapping_line)
11174 char mapping_guid[MAX_LINE_LEN];
11175 char *mapping_start, *mapping_end;
11177 // get GUID from game controller mapping line: copy complete line
11178 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11179 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11181 // get GUID from game controller mapping line: cut after GUID part
11182 mapping_start = strchr(mapping_guid, ',');
11183 if (mapping_start != NULL)
11184 *mapping_start = '\0';
11186 // cut newline from game controller mapping line
11187 mapping_end = strchr(mapping_line, '\n');
11188 if (mapping_end != NULL)
11189 *mapping_end = '\0';
11191 // add mapping entry to game controller mappings hash
11192 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11195 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11200 if (!(file = fopen(filename, MODE_READ)))
11202 Warn("cannot read game controller mappings file '%s'", filename);
11207 while (!feof(file))
11209 char line[MAX_LINE_LEN];
11211 if (!fgets(line, MAX_LINE_LEN, file))
11214 addGameControllerMappingToHash(mappings_hash, line);
11220 void SaveSetup_Default(void)
11222 char *filename = getSetupFilename();
11226 InitUserDataDirectory();
11228 if (!(file = fopen(filename, MODE_WRITE)))
11230 Warn("cannot write setup file '%s'", filename);
11235 fprintFileHeader(file, SETUP_FILENAME);
11237 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11239 // just to make things nicer :)
11240 if (global_setup_tokens[i].value == &setup.multiple_users ||
11241 global_setup_tokens[i].value == &setup.sound ||
11242 global_setup_tokens[i].value == &setup.graphics_set ||
11243 global_setup_tokens[i].value == &setup.volume_simple ||
11244 global_setup_tokens[i].value == &setup.network_mode ||
11245 global_setup_tokens[i].value == &setup.touch.control_type ||
11246 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11247 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11248 fprintf(file, "\n");
11250 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11253 for (i = 0; i < 2; i++)
11255 int grid_xsize = setup.touch.grid_xsize[i];
11256 int grid_ysize = setup.touch.grid_ysize[i];
11259 fprintf(file, "\n");
11261 for (y = 0; y < grid_ysize; y++)
11263 char token_string[MAX_LINE_LEN];
11264 char value_string[MAX_LINE_LEN];
11266 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11268 for (x = 0; x < grid_xsize; x++)
11270 char c = setup.touch.grid_button[i][x][y];
11272 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11275 value_string[grid_xsize] = '\0';
11277 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11281 fprintf(file, "\n");
11282 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11283 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11285 fprintf(file, "\n");
11286 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11287 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11289 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11293 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11294 fprintf(file, "\n");
11296 setup_input = setup.input[pnr];
11297 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11298 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11301 fprintf(file, "\n");
11302 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11303 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11305 // (internal setup values not saved to user setup file)
11307 fprintf(file, "\n");
11308 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11309 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11310 setup.debug.xsn_mode != AUTO)
11311 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11313 fprintf(file, "\n");
11314 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11315 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11319 SetFilePermissions(filename, PERMS_PRIVATE);
11322 void SaveSetup_AutoSetup(void)
11324 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11328 InitUserDataDirectory();
11330 if (!(file = fopen(filename, MODE_WRITE)))
11332 Warn("cannot write auto setup file '%s'", filename);
11339 fprintFileHeader(file, AUTOSETUP_FILENAME);
11341 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11342 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11346 SetFilePermissions(filename, PERMS_PRIVATE);
11351 void SaveSetup_ServerSetup(void)
11353 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11357 InitUserDataDirectory();
11359 if (!(file = fopen(filename, MODE_WRITE)))
11361 Warn("cannot write server setup file '%s'", filename);
11368 fprintFileHeader(file, SERVERSETUP_FILENAME);
11370 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11372 // just to make things nicer :)
11373 if (server_setup_tokens[i].value == &setup.use_api_server)
11374 fprintf(file, "\n");
11376 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11381 SetFilePermissions(filename, PERMS_PRIVATE);
11386 void SaveSetup_EditorCascade(void)
11388 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11392 InitUserDataDirectory();
11394 if (!(file = fopen(filename, MODE_WRITE)))
11396 Warn("cannot write editor cascade state file '%s'", filename);
11403 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11405 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11406 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11410 SetFilePermissions(filename, PERMS_PRIVATE);
11415 void SaveSetup(void)
11417 SaveSetup_Default();
11418 SaveSetup_AutoSetup();
11419 SaveSetup_ServerSetup();
11420 SaveSetup_EditorCascade();
11423 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11428 if (!(file = fopen(filename, MODE_WRITE)))
11430 Warn("cannot write game controller mappings file '%s'", filename);
11435 BEGIN_HASH_ITERATION(mappings_hash, itr)
11437 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11439 END_HASH_ITERATION(mappings_hash, itr)
11444 void SaveSetup_AddGameControllerMapping(char *mapping)
11446 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11447 SetupFileHash *mappings_hash = newSetupFileHash();
11449 InitUserDataDirectory();
11451 // load existing personal game controller mappings
11452 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11454 // add new mapping to personal game controller mappings
11455 addGameControllerMappingToHash(mappings_hash, mapping);
11457 // save updated personal game controller mappings
11458 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11460 freeSetupFileHash(mappings_hash);
11464 void LoadCustomElementDescriptions(void)
11466 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11467 SetupFileHash *setup_file_hash;
11470 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11472 if (element_info[i].custom_description != NULL)
11474 free(element_info[i].custom_description);
11475 element_info[i].custom_description = NULL;
11479 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11482 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11484 char *token = getStringCat2(element_info[i].token_name, ".name");
11485 char *value = getHashEntry(setup_file_hash, token);
11488 element_info[i].custom_description = getStringCopy(value);
11493 freeSetupFileHash(setup_file_hash);
11496 static int getElementFromToken(char *token)
11498 char *value = getHashEntry(element_token_hash, token);
11501 return atoi(value);
11503 Warn("unknown element token '%s'", token);
11505 return EL_UNDEFINED;
11508 void FreeGlobalAnimEventInfo(void)
11510 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11512 if (gaei->event_list == NULL)
11517 for (i = 0; i < gaei->num_event_lists; i++)
11519 checked_free(gaei->event_list[i]->event_value);
11520 checked_free(gaei->event_list[i]);
11523 checked_free(gaei->event_list);
11525 gaei->event_list = NULL;
11526 gaei->num_event_lists = 0;
11529 static int AddGlobalAnimEventList(void)
11531 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11532 int list_pos = gaei->num_event_lists++;
11534 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11535 sizeof(struct GlobalAnimEventListInfo *));
11537 gaei->event_list[list_pos] =
11538 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11540 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11542 gaeli->event_value = NULL;
11543 gaeli->num_event_values = 0;
11548 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11550 // do not add empty global animation events
11551 if (event_value == ANIM_EVENT_NONE)
11554 // if list position is undefined, create new list
11555 if (list_pos == ANIM_EVENT_UNDEFINED)
11556 list_pos = AddGlobalAnimEventList();
11558 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11559 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11560 int value_pos = gaeli->num_event_values++;
11562 gaeli->event_value = checked_realloc(gaeli->event_value,
11563 gaeli->num_event_values * sizeof(int *));
11565 gaeli->event_value[value_pos] = event_value;
11570 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11572 if (list_pos == ANIM_EVENT_UNDEFINED)
11573 return ANIM_EVENT_NONE;
11575 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11576 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11578 return gaeli->event_value[value_pos];
11581 int GetGlobalAnimEventValueCount(int list_pos)
11583 if (list_pos == ANIM_EVENT_UNDEFINED)
11586 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11587 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11589 return gaeli->num_event_values;
11592 // This function checks if a string <s> of the format "string1, string2, ..."
11593 // exactly contains a string <s_contained>.
11595 static boolean string_has_parameter(char *s, char *s_contained)
11599 if (s == NULL || s_contained == NULL)
11602 if (strlen(s_contained) > strlen(s))
11605 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11607 char next_char = s[strlen(s_contained)];
11609 // check if next character is delimiter or whitespace
11610 if (next_char == ',' || next_char == '\0' ||
11611 next_char == ' ' || next_char == '\t')
11615 // check if string contains another parameter string after a comma
11616 substring = strchr(s, ',');
11617 if (substring == NULL) // string does not contain a comma
11620 // advance string pointer to next character after the comma
11623 // skip potential whitespaces after the comma
11624 while (*substring == ' ' || *substring == '\t')
11627 return string_has_parameter(substring, s_contained);
11630 static int get_anim_parameter_value_ce(char *s)
11633 char *pattern_1 = "ce_change:custom_";
11634 char *pattern_2 = ".page_";
11635 int pattern_1_len = strlen(pattern_1);
11636 char *matching_char = strstr(s_ptr, pattern_1);
11637 int result = ANIM_EVENT_NONE;
11639 if (matching_char == NULL)
11640 return ANIM_EVENT_NONE;
11642 result = ANIM_EVENT_CE_CHANGE;
11644 s_ptr = matching_char + pattern_1_len;
11646 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11647 if (*s_ptr >= '0' && *s_ptr <= '9')
11649 int gic_ce_nr = (*s_ptr++ - '0');
11651 if (*s_ptr >= '0' && *s_ptr <= '9')
11653 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11655 if (*s_ptr >= '0' && *s_ptr <= '9')
11656 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11659 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11660 return ANIM_EVENT_NONE;
11662 // custom element stored as 0 to 255
11665 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11669 // invalid custom element number specified
11671 return ANIM_EVENT_NONE;
11674 // check for change page number ("page_X" or "page_XX") (optional)
11675 if (strPrefix(s_ptr, pattern_2))
11677 s_ptr += strlen(pattern_2);
11679 if (*s_ptr >= '0' && *s_ptr <= '9')
11681 int gic_page_nr = (*s_ptr++ - '0');
11683 if (*s_ptr >= '0' && *s_ptr <= '9')
11684 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11686 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11687 return ANIM_EVENT_NONE;
11689 // change page stored as 1 to 32 (0 means "all change pages")
11691 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11695 // invalid animation part number specified
11697 return ANIM_EVENT_NONE;
11701 // discard result if next character is neither delimiter nor whitespace
11702 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11703 *s_ptr == ' ' || *s_ptr == '\t'))
11704 return ANIM_EVENT_NONE;
11709 static int get_anim_parameter_value(char *s)
11711 int event_value[] =
11719 char *pattern_1[] =
11727 char *pattern_2 = ".part_";
11728 char *matching_char = NULL;
11730 int pattern_1_len = 0;
11731 int result = ANIM_EVENT_NONE;
11734 result = get_anim_parameter_value_ce(s);
11736 if (result != ANIM_EVENT_NONE)
11739 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11741 matching_char = strstr(s_ptr, pattern_1[i]);
11742 pattern_1_len = strlen(pattern_1[i]);
11743 result = event_value[i];
11745 if (matching_char != NULL)
11749 if (matching_char == NULL)
11750 return ANIM_EVENT_NONE;
11752 s_ptr = matching_char + pattern_1_len;
11754 // check for main animation number ("anim_X" or "anim_XX")
11755 if (*s_ptr >= '0' && *s_ptr <= '9')
11757 int gic_anim_nr = (*s_ptr++ - '0');
11759 if (*s_ptr >= '0' && *s_ptr <= '9')
11760 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11762 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11763 return ANIM_EVENT_NONE;
11765 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11769 // invalid main animation number specified
11771 return ANIM_EVENT_NONE;
11774 // check for animation part number ("part_X" or "part_XX") (optional)
11775 if (strPrefix(s_ptr, pattern_2))
11777 s_ptr += strlen(pattern_2);
11779 if (*s_ptr >= '0' && *s_ptr <= '9')
11781 int gic_part_nr = (*s_ptr++ - '0');
11783 if (*s_ptr >= '0' && *s_ptr <= '9')
11784 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11786 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11787 return ANIM_EVENT_NONE;
11789 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11793 // invalid animation part number specified
11795 return ANIM_EVENT_NONE;
11799 // discard result if next character is neither delimiter nor whitespace
11800 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11801 *s_ptr == ' ' || *s_ptr == '\t'))
11802 return ANIM_EVENT_NONE;
11807 static int get_anim_parameter_values(char *s)
11809 int list_pos = ANIM_EVENT_UNDEFINED;
11810 int event_value = ANIM_EVENT_DEFAULT;
11812 if (string_has_parameter(s, "any"))
11813 event_value |= ANIM_EVENT_ANY;
11815 if (string_has_parameter(s, "click:self") ||
11816 string_has_parameter(s, "click") ||
11817 string_has_parameter(s, "self"))
11818 event_value |= ANIM_EVENT_SELF;
11820 if (string_has_parameter(s, "unclick:any"))
11821 event_value |= ANIM_EVENT_UNCLICK_ANY;
11823 // if animation event found, add it to global animation event list
11824 if (event_value != ANIM_EVENT_NONE)
11825 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11829 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11830 event_value = get_anim_parameter_value(s);
11832 // if animation event found, add it to global animation event list
11833 if (event_value != ANIM_EVENT_NONE)
11834 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11836 // continue with next part of the string, starting with next comma
11837 s = strchr(s + 1, ',');
11843 static int get_anim_action_parameter_value(char *token)
11845 // check most common default case first to massively speed things up
11846 if (strEqual(token, ARG_UNDEFINED))
11847 return ANIM_EVENT_ACTION_NONE;
11849 int result = getImageIDFromToken(token);
11853 char *gfx_token = getStringCat2("gfx.", token);
11855 result = getImageIDFromToken(gfx_token);
11857 checked_free(gfx_token);
11862 Key key = getKeyFromX11KeyName(token);
11864 if (key != KSYM_UNDEFINED)
11865 result = -(int)key;
11872 result = get_hash_from_key(token); // unsigned int => int
11873 result = ABS(result); // may be negative now
11874 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11876 setHashEntry(anim_url_hash, int2str(result, 0), token);
11881 result = ANIM_EVENT_ACTION_NONE;
11886 int get_parameter_value(char *value_raw, char *suffix, int type)
11888 char *value = getStringToLower(value_raw);
11889 int result = 0; // probably a save default value
11891 if (strEqual(suffix, ".direction"))
11893 result = (strEqual(value, "left") ? MV_LEFT :
11894 strEqual(value, "right") ? MV_RIGHT :
11895 strEqual(value, "up") ? MV_UP :
11896 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11898 else if (strEqual(suffix, ".position"))
11900 result = (strEqual(value, "left") ? POS_LEFT :
11901 strEqual(value, "right") ? POS_RIGHT :
11902 strEqual(value, "top") ? POS_TOP :
11903 strEqual(value, "upper") ? POS_UPPER :
11904 strEqual(value, "middle") ? POS_MIDDLE :
11905 strEqual(value, "lower") ? POS_LOWER :
11906 strEqual(value, "bottom") ? POS_BOTTOM :
11907 strEqual(value, "any") ? POS_ANY :
11908 strEqual(value, "ce") ? POS_CE :
11909 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
11910 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11912 else if (strEqual(suffix, ".align"))
11914 result = (strEqual(value, "left") ? ALIGN_LEFT :
11915 strEqual(value, "right") ? ALIGN_RIGHT :
11916 strEqual(value, "center") ? ALIGN_CENTER :
11917 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11919 else if (strEqual(suffix, ".valign"))
11921 result = (strEqual(value, "top") ? VALIGN_TOP :
11922 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11923 strEqual(value, "middle") ? VALIGN_MIDDLE :
11924 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11926 else if (strEqual(suffix, ".anim_mode"))
11928 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11929 string_has_parameter(value, "loop") ? ANIM_LOOP :
11930 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11931 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11932 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11933 string_has_parameter(value, "random") ? ANIM_RANDOM :
11934 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11935 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11936 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11937 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11938 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11939 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11940 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11941 string_has_parameter(value, "all") ? ANIM_ALL :
11942 string_has_parameter(value, "tiled") ? ANIM_TILED :
11943 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
11946 if (string_has_parameter(value, "once"))
11947 result |= ANIM_ONCE;
11949 if (string_has_parameter(value, "reverse"))
11950 result |= ANIM_REVERSE;
11952 if (string_has_parameter(value, "opaque_player"))
11953 result |= ANIM_OPAQUE_PLAYER;
11955 if (string_has_parameter(value, "static_panel"))
11956 result |= ANIM_STATIC_PANEL;
11958 else if (strEqual(suffix, ".init_event") ||
11959 strEqual(suffix, ".anim_event"))
11961 result = get_anim_parameter_values(value);
11963 else if (strEqual(suffix, ".init_delay_action") ||
11964 strEqual(suffix, ".anim_delay_action") ||
11965 strEqual(suffix, ".post_delay_action") ||
11966 strEqual(suffix, ".init_event_action") ||
11967 strEqual(suffix, ".anim_event_action"))
11969 result = get_anim_action_parameter_value(value_raw);
11971 else if (strEqual(suffix, ".class"))
11973 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11974 get_hash_from_key(value));
11976 else if (strEqual(suffix, ".style"))
11978 result = STYLE_DEFAULT;
11980 if (string_has_parameter(value, "accurate_borders"))
11981 result |= STYLE_ACCURATE_BORDERS;
11983 if (string_has_parameter(value, "inner_corners"))
11984 result |= STYLE_INNER_CORNERS;
11986 if (string_has_parameter(value, "reverse"))
11987 result |= STYLE_REVERSE;
11989 if (string_has_parameter(value, "leftmost_position"))
11990 result |= STYLE_LEFTMOST_POSITION;
11992 if (string_has_parameter(value, "block_clicks"))
11993 result |= STYLE_BLOCK;
11995 if (string_has_parameter(value, "passthrough_clicks"))
11996 result |= STYLE_PASSTHROUGH;
11998 if (string_has_parameter(value, "multiple_actions"))
11999 result |= STYLE_MULTIPLE_ACTIONS;
12001 if (string_has_parameter(value, "consume_ce_event"))
12002 result |= STYLE_CONSUME_CE_EVENT;
12004 else if (strEqual(suffix, ".fade_mode"))
12006 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12007 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12008 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12009 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12010 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12011 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12012 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12013 FADE_MODE_DEFAULT);
12015 else if (strEqual(suffix, ".auto_delay_unit"))
12017 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12018 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12019 AUTO_DELAY_UNIT_DEFAULT);
12021 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12023 result = gfx.get_font_from_token_function(value);
12025 else // generic parameter of type integer or boolean
12027 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12028 type == TYPE_INTEGER ? get_integer_from_string(value) :
12029 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12030 ARG_UNDEFINED_VALUE);
12038 static int get_token_parameter_value(char *token, char *value_raw)
12042 if (token == NULL || value_raw == NULL)
12043 return ARG_UNDEFINED_VALUE;
12045 suffix = strrchr(token, '.');
12046 if (suffix == NULL)
12049 if (strEqual(suffix, ".element"))
12050 return getElementFromToken(value_raw);
12052 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12053 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12056 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12057 boolean ignore_defaults)
12061 for (i = 0; image_config_vars[i].token != NULL; i++)
12063 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12065 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12066 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12070 *image_config_vars[i].value =
12071 get_token_parameter_value(image_config_vars[i].token, value);
12075 void InitMenuDesignSettings_Static(void)
12077 // always start with reliable default values from static default config
12078 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12081 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12085 // the following initializes hierarchical values from static configuration
12087 // special case: initialize "ARG_DEFAULT" values in static default config
12088 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12089 titlescreen_initial_first_default.fade_mode =
12090 title_initial_first_default.fade_mode;
12091 titlescreen_initial_first_default.fade_delay =
12092 title_initial_first_default.fade_delay;
12093 titlescreen_initial_first_default.post_delay =
12094 title_initial_first_default.post_delay;
12095 titlescreen_initial_first_default.auto_delay =
12096 title_initial_first_default.auto_delay;
12097 titlescreen_initial_first_default.auto_delay_unit =
12098 title_initial_first_default.auto_delay_unit;
12099 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12100 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12101 titlescreen_first_default.post_delay = title_first_default.post_delay;
12102 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12103 titlescreen_first_default.auto_delay_unit =
12104 title_first_default.auto_delay_unit;
12105 titlemessage_initial_first_default.fade_mode =
12106 title_initial_first_default.fade_mode;
12107 titlemessage_initial_first_default.fade_delay =
12108 title_initial_first_default.fade_delay;
12109 titlemessage_initial_first_default.post_delay =
12110 title_initial_first_default.post_delay;
12111 titlemessage_initial_first_default.auto_delay =
12112 title_initial_first_default.auto_delay;
12113 titlemessage_initial_first_default.auto_delay_unit =
12114 title_initial_first_default.auto_delay_unit;
12115 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12116 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12117 titlemessage_first_default.post_delay = title_first_default.post_delay;
12118 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12119 titlemessage_first_default.auto_delay_unit =
12120 title_first_default.auto_delay_unit;
12122 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12123 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12124 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12125 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12126 titlescreen_initial_default.auto_delay_unit =
12127 title_initial_default.auto_delay_unit;
12128 titlescreen_default.fade_mode = title_default.fade_mode;
12129 titlescreen_default.fade_delay = title_default.fade_delay;
12130 titlescreen_default.post_delay = title_default.post_delay;
12131 titlescreen_default.auto_delay = title_default.auto_delay;
12132 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12133 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12134 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12135 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12136 titlemessage_initial_default.auto_delay_unit =
12137 title_initial_default.auto_delay_unit;
12138 titlemessage_default.fade_mode = title_default.fade_mode;
12139 titlemessage_default.fade_delay = title_default.fade_delay;
12140 titlemessage_default.post_delay = title_default.post_delay;
12141 titlemessage_default.auto_delay = title_default.auto_delay;
12142 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12144 // special case: initialize "ARG_DEFAULT" values in static default config
12145 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12146 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12148 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12149 titlescreen_first[i] = titlescreen_first_default;
12150 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12151 titlemessage_first[i] = titlemessage_first_default;
12153 titlescreen_initial[i] = titlescreen_initial_default;
12154 titlescreen[i] = titlescreen_default;
12155 titlemessage_initial[i] = titlemessage_initial_default;
12156 titlemessage[i] = titlemessage_default;
12159 // special case: initialize "ARG_DEFAULT" values in static default config
12160 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12161 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12163 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12166 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12167 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12168 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12171 // special case: initialize "ARG_DEFAULT" values in static default config
12172 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12173 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12175 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12176 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12177 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12179 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12182 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12186 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12190 struct XY *dst, *src;
12192 game_buttons_xy[] =
12194 { &game.button.save, &game.button.stop },
12195 { &game.button.pause2, &game.button.pause },
12196 { &game.button.load, &game.button.play },
12197 { &game.button.undo, &game.button.stop },
12198 { &game.button.redo, &game.button.play },
12204 // special case: initialize later added SETUP list size from LEVELS value
12205 if (menu.list_size[GAME_MODE_SETUP] == -1)
12206 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12208 // set default position for snapshot buttons to stop/pause/play buttons
12209 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12210 if ((*game_buttons_xy[i].dst).x == -1 &&
12211 (*game_buttons_xy[i].dst).y == -1)
12212 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12214 // --------------------------------------------------------------------------
12215 // dynamic viewports (including playfield margins, borders and alignments)
12216 // --------------------------------------------------------------------------
12218 // dynamic viewports currently only supported for landscape mode
12219 int display_width = MAX(video.display_width, video.display_height);
12220 int display_height = MIN(video.display_width, video.display_height);
12222 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12224 struct RectWithBorder *vp_window = &viewport.window[i];
12225 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12226 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12227 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12228 boolean dynamic_window_width = (vp_window->min_width != -1);
12229 boolean dynamic_window_height = (vp_window->min_height != -1);
12230 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12231 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12233 // adjust window size if min/max width/height is specified
12235 if (vp_window->min_width != -1)
12237 int window_width = display_width;
12239 // when using static window height, use aspect ratio of display
12240 if (vp_window->min_height == -1)
12241 window_width = vp_window->height * display_width / display_height;
12243 vp_window->width = MAX(vp_window->min_width, window_width);
12246 if (vp_window->min_height != -1)
12248 int window_height = display_height;
12250 // when using static window width, use aspect ratio of display
12251 if (vp_window->min_width == -1)
12252 window_height = vp_window->width * display_height / display_width;
12254 vp_window->height = MAX(vp_window->min_height, window_height);
12257 if (vp_window->max_width != -1)
12258 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12260 if (vp_window->max_height != -1)
12261 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12263 int playfield_width = vp_window->width;
12264 int playfield_height = vp_window->height;
12266 // adjust playfield size and position according to specified margins
12268 playfield_width -= vp_playfield->margin_left;
12269 playfield_width -= vp_playfield->margin_right;
12271 playfield_height -= vp_playfield->margin_top;
12272 playfield_height -= vp_playfield->margin_bottom;
12274 // adjust playfield size if min/max width/height is specified
12276 if (vp_playfield->min_width != -1)
12277 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12279 if (vp_playfield->min_height != -1)
12280 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12282 if (vp_playfield->max_width != -1)
12283 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12285 if (vp_playfield->max_height != -1)
12286 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12288 // adjust playfield position according to specified alignment
12290 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12291 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12292 else if (vp_playfield->align == ALIGN_CENTER)
12293 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12294 else if (vp_playfield->align == ALIGN_RIGHT)
12295 vp_playfield->x += playfield_width - vp_playfield->width;
12297 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12298 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12299 else if (vp_playfield->valign == VALIGN_MIDDLE)
12300 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12301 else if (vp_playfield->valign == VALIGN_BOTTOM)
12302 vp_playfield->y += playfield_height - vp_playfield->height;
12304 vp_playfield->x += vp_playfield->margin_left;
12305 vp_playfield->y += vp_playfield->margin_top;
12307 // adjust individual playfield borders if only default border is specified
12309 if (vp_playfield->border_left == -1)
12310 vp_playfield->border_left = vp_playfield->border_size;
12311 if (vp_playfield->border_right == -1)
12312 vp_playfield->border_right = vp_playfield->border_size;
12313 if (vp_playfield->border_top == -1)
12314 vp_playfield->border_top = vp_playfield->border_size;
12315 if (vp_playfield->border_bottom == -1)
12316 vp_playfield->border_bottom = vp_playfield->border_size;
12318 // set dynamic playfield borders if borders are specified as undefined
12319 // (but only if window size was dynamic and playfield size was static)
12321 if (dynamic_window_width && !dynamic_playfield_width)
12323 if (vp_playfield->border_left == -1)
12325 vp_playfield->border_left = (vp_playfield->x -
12326 vp_playfield->margin_left);
12327 vp_playfield->x -= vp_playfield->border_left;
12328 vp_playfield->width += vp_playfield->border_left;
12331 if (vp_playfield->border_right == -1)
12333 vp_playfield->border_right = (vp_window->width -
12335 vp_playfield->width -
12336 vp_playfield->margin_right);
12337 vp_playfield->width += vp_playfield->border_right;
12341 if (dynamic_window_height && !dynamic_playfield_height)
12343 if (vp_playfield->border_top == -1)
12345 vp_playfield->border_top = (vp_playfield->y -
12346 vp_playfield->margin_top);
12347 vp_playfield->y -= vp_playfield->border_top;
12348 vp_playfield->height += vp_playfield->border_top;
12351 if (vp_playfield->border_bottom == -1)
12353 vp_playfield->border_bottom = (vp_window->height -
12355 vp_playfield->height -
12356 vp_playfield->margin_bottom);
12357 vp_playfield->height += vp_playfield->border_bottom;
12361 // adjust playfield size to be a multiple of a defined alignment tile size
12363 int align_size = vp_playfield->align_size;
12364 int playfield_xtiles = vp_playfield->width / align_size;
12365 int playfield_ytiles = vp_playfield->height / align_size;
12366 int playfield_width_corrected = playfield_xtiles * align_size;
12367 int playfield_height_corrected = playfield_ytiles * align_size;
12368 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12369 i == GFX_SPECIAL_ARG_EDITOR);
12371 if (is_playfield_mode &&
12372 dynamic_playfield_width &&
12373 vp_playfield->width != playfield_width_corrected)
12375 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12377 vp_playfield->width = playfield_width_corrected;
12379 if (vp_playfield->align == ALIGN_LEFT)
12381 vp_playfield->border_left += playfield_xdiff;
12383 else if (vp_playfield->align == ALIGN_RIGHT)
12385 vp_playfield->border_right += playfield_xdiff;
12387 else if (vp_playfield->align == ALIGN_CENTER)
12389 int border_left_diff = playfield_xdiff / 2;
12390 int border_right_diff = playfield_xdiff - border_left_diff;
12392 vp_playfield->border_left += border_left_diff;
12393 vp_playfield->border_right += border_right_diff;
12397 if (is_playfield_mode &&
12398 dynamic_playfield_height &&
12399 vp_playfield->height != playfield_height_corrected)
12401 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12403 vp_playfield->height = playfield_height_corrected;
12405 if (vp_playfield->valign == VALIGN_TOP)
12407 vp_playfield->border_top += playfield_ydiff;
12409 else if (vp_playfield->align == VALIGN_BOTTOM)
12411 vp_playfield->border_right += playfield_ydiff;
12413 else if (vp_playfield->align == VALIGN_MIDDLE)
12415 int border_top_diff = playfield_ydiff / 2;
12416 int border_bottom_diff = playfield_ydiff - border_top_diff;
12418 vp_playfield->border_top += border_top_diff;
12419 vp_playfield->border_bottom += border_bottom_diff;
12423 // adjust door positions according to specified alignment
12425 for (j = 0; j < 2; j++)
12427 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12429 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12430 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12431 else if (vp_door->align == ALIGN_CENTER)
12432 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12433 else if (vp_door->align == ALIGN_RIGHT)
12434 vp_door->x += vp_window->width - vp_door->width;
12436 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12437 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12438 else if (vp_door->valign == VALIGN_MIDDLE)
12439 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12440 else if (vp_door->valign == VALIGN_BOTTOM)
12441 vp_door->y += vp_window->height - vp_door->height;
12446 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12450 struct XYTileSize *dst, *src;
12453 editor_buttons_xy[] =
12456 &editor.button.element_left, &editor.palette.element_left,
12457 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12460 &editor.button.element_middle, &editor.palette.element_middle,
12461 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12464 &editor.button.element_right, &editor.palette.element_right,
12465 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12472 // set default position for element buttons to element graphics
12473 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12475 if ((*editor_buttons_xy[i].dst).x == -1 &&
12476 (*editor_buttons_xy[i].dst).y == -1)
12478 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12480 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12482 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12486 // adjust editor palette rows and columns if specified to be dynamic
12488 if (editor.palette.cols == -1)
12490 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12491 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12492 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12494 editor.palette.cols = (vp_width - sc_width) / bt_width;
12496 if (editor.palette.x == -1)
12498 int palette_width = editor.palette.cols * bt_width + sc_width;
12500 editor.palette.x = (vp_width - palette_width) / 2;
12504 if (editor.palette.rows == -1)
12506 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12507 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12508 int tx_height = getFontHeight(FONT_TEXT_2);
12510 editor.palette.rows = (vp_height - tx_height) / bt_height;
12512 if (editor.palette.y == -1)
12514 int palette_height = editor.palette.rows * bt_height + tx_height;
12516 editor.palette.y = (vp_height - palette_height) / 2;
12521 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12522 boolean initialize)
12524 // special case: check if network and preview player positions are redefined,
12525 // to compare this later against the main menu level preview being redefined
12526 struct TokenIntPtrInfo menu_config_players[] =
12528 { "main.network_players.x", &menu.main.network_players.redefined },
12529 { "main.network_players.y", &menu.main.network_players.redefined },
12530 { "main.preview_players.x", &menu.main.preview_players.redefined },
12531 { "main.preview_players.y", &menu.main.preview_players.redefined },
12532 { "preview.x", &preview.redefined },
12533 { "preview.y", &preview.redefined }
12539 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12540 *menu_config_players[i].value = FALSE;
12544 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12545 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12546 *menu_config_players[i].value = TRUE;
12550 static void InitMenuDesignSettings_PreviewPlayers(void)
12552 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12555 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12557 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12560 static void LoadMenuDesignSettingsFromFilename(char *filename)
12562 static struct TitleFadingInfo tfi;
12563 static struct TitleMessageInfo tmi;
12564 static struct TokenInfo title_tokens[] =
12566 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12567 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12568 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12569 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12570 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12574 static struct TokenInfo titlemessage_tokens[] =
12576 { TYPE_INTEGER, &tmi.x, ".x" },
12577 { TYPE_INTEGER, &tmi.y, ".y" },
12578 { TYPE_INTEGER, &tmi.width, ".width" },
12579 { TYPE_INTEGER, &tmi.height, ".height" },
12580 { TYPE_INTEGER, &tmi.chars, ".chars" },
12581 { TYPE_INTEGER, &tmi.lines, ".lines" },
12582 { TYPE_INTEGER, &tmi.align, ".align" },
12583 { TYPE_INTEGER, &tmi.valign, ".valign" },
12584 { TYPE_INTEGER, &tmi.font, ".font" },
12585 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12586 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12587 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12588 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12589 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12590 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12591 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12592 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12593 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12599 struct TitleFadingInfo *info;
12604 // initialize first titles from "enter screen" definitions, if defined
12605 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12606 { &title_first_default, "menu.enter_screen.TITLE" },
12608 // initialize title screens from "next screen" definitions, if defined
12609 { &title_initial_default, "menu.next_screen.TITLE" },
12610 { &title_default, "menu.next_screen.TITLE" },
12616 struct TitleMessageInfo *array;
12619 titlemessage_arrays[] =
12621 // initialize first titles from "enter screen" definitions, if defined
12622 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12623 { titlescreen_first, "menu.enter_screen.TITLE" },
12624 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12625 { titlemessage_first, "menu.enter_screen.TITLE" },
12627 // initialize titles from "next screen" definitions, if defined
12628 { titlescreen_initial, "menu.next_screen.TITLE" },
12629 { titlescreen, "menu.next_screen.TITLE" },
12630 { titlemessage_initial, "menu.next_screen.TITLE" },
12631 { titlemessage, "menu.next_screen.TITLE" },
12633 // overwrite titles with title definitions, if defined
12634 { titlescreen_initial_first, "[title_initial]" },
12635 { titlescreen_first, "[title]" },
12636 { titlemessage_initial_first, "[title_initial]" },
12637 { titlemessage_first, "[title]" },
12639 { titlescreen_initial, "[title_initial]" },
12640 { titlescreen, "[title]" },
12641 { titlemessage_initial, "[title_initial]" },
12642 { titlemessage, "[title]" },
12644 // overwrite titles with title screen/message definitions, if defined
12645 { titlescreen_initial_first, "[titlescreen_initial]" },
12646 { titlescreen_first, "[titlescreen]" },
12647 { titlemessage_initial_first, "[titlemessage_initial]" },
12648 { titlemessage_first, "[titlemessage]" },
12650 { titlescreen_initial, "[titlescreen_initial]" },
12651 { titlescreen, "[titlescreen]" },
12652 { titlemessage_initial, "[titlemessage_initial]" },
12653 { titlemessage, "[titlemessage]" },
12657 SetupFileHash *setup_file_hash;
12660 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12663 // the following initializes hierarchical values from dynamic configuration
12665 // special case: initialize with default values that may be overwritten
12666 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12667 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12669 struct TokenIntPtrInfo menu_config[] =
12671 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12672 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12673 { "menu.list_size", &menu.list_size[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.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12688 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12690 struct TokenIntPtrInfo menu_config[] =
12692 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12693 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12694 { "menu.list_size.INFO", &menu.list_size_info[i] },
12695 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12696 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12699 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12701 char *token = menu_config[j].token;
12702 char *value = getHashEntry(setup_file_hash, token);
12705 *menu_config[j].value = get_integer_from_string(value);
12709 // special case: initialize with default values that may be overwritten
12710 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12711 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12713 struct TokenIntPtrInfo menu_config[] =
12715 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12716 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12719 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12721 char *token = menu_config[j].token;
12722 char *value = getHashEntry(setup_file_hash, token);
12725 *menu_config[j].value = get_integer_from_string(value);
12729 // special case: initialize with default values that may be overwritten
12730 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12731 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12733 struct TokenIntPtrInfo menu_config[] =
12735 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12736 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12737 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12738 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12739 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12740 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12741 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12742 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12743 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12744 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12747 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12749 char *token = menu_config[j].token;
12750 char *value = getHashEntry(setup_file_hash, token);
12753 *menu_config[j].value = get_integer_from_string(value);
12757 // special case: initialize with default values that may be overwritten
12758 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12759 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12761 struct TokenIntPtrInfo menu_config[] =
12763 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12764 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12765 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12766 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12767 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12768 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12769 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12770 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12771 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12774 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12776 char *token = menu_config[j].token;
12777 char *value = getHashEntry(setup_file_hash, token);
12780 *menu_config[j].value = get_token_parameter_value(token, value);
12784 // special case: initialize with default values that may be overwritten
12785 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12786 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12790 char *token_prefix;
12791 struct RectWithBorder *struct_ptr;
12795 { "viewport.window", &viewport.window[i] },
12796 { "viewport.playfield", &viewport.playfield[i] },
12797 { "viewport.door_1", &viewport.door_1[i] },
12798 { "viewport.door_2", &viewport.door_2[i] }
12801 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12803 struct TokenIntPtrInfo vp_config[] =
12805 { ".x", &vp_struct[j].struct_ptr->x },
12806 { ".y", &vp_struct[j].struct_ptr->y },
12807 { ".width", &vp_struct[j].struct_ptr->width },
12808 { ".height", &vp_struct[j].struct_ptr->height },
12809 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12810 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12811 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12812 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12813 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12814 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12815 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12816 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12817 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12818 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12819 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12820 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12821 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12822 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12823 { ".align", &vp_struct[j].struct_ptr->align },
12824 { ".valign", &vp_struct[j].struct_ptr->valign }
12827 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12829 char *token = getStringCat2(vp_struct[j].token_prefix,
12830 vp_config[k].token);
12831 char *value = getHashEntry(setup_file_hash, token);
12834 *vp_config[k].value = get_token_parameter_value(token, value);
12841 // special case: initialize with default values that may be overwritten
12842 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12843 for (i = 0; title_info[i].info != NULL; i++)
12845 struct TitleFadingInfo *info = title_info[i].info;
12846 char *base_token = title_info[i].text;
12848 for (j = 0; title_tokens[j].type != -1; j++)
12850 char *token = getStringCat2(base_token, title_tokens[j].text);
12851 char *value = getHashEntry(setup_file_hash, token);
12855 int parameter_value = get_token_parameter_value(token, value);
12859 *(int *)title_tokens[j].value = (int)parameter_value;
12868 // special case: initialize with default values that may be overwritten
12869 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12870 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12872 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12873 char *base_token = titlemessage_arrays[i].text;
12875 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12877 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12878 char *value = getHashEntry(setup_file_hash, token);
12882 int parameter_value = get_token_parameter_value(token, value);
12884 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12888 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12889 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12891 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12901 // read (and overwrite with) values that may be specified in config file
12902 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12904 // special case: check if network and preview player positions are redefined
12905 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
12907 freeSetupFileHash(setup_file_hash);
12910 void LoadMenuDesignSettings(void)
12912 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12914 InitMenuDesignSettings_Static();
12915 InitMenuDesignSettings_SpecialPreProcessing();
12916 InitMenuDesignSettings_PreviewPlayers();
12918 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12920 // first look for special settings configured in level series config
12921 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12923 if (fileExists(filename_base))
12924 LoadMenuDesignSettingsFromFilename(filename_base);
12927 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12929 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12930 LoadMenuDesignSettingsFromFilename(filename_local);
12932 InitMenuDesignSettings_SpecialPostProcessing();
12935 void LoadMenuDesignSettings_AfterGraphics(void)
12937 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12940 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
12941 boolean ignore_defaults)
12945 for (i = 0; sound_config_vars[i].token != NULL; i++)
12947 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
12949 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12950 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12954 *sound_config_vars[i].value =
12955 get_token_parameter_value(sound_config_vars[i].token, value);
12959 void InitSoundSettings_Static(void)
12961 // always start with reliable default values from static default config
12962 InitSoundSettings_FromHash(sound_config_hash, FALSE);
12965 static void LoadSoundSettingsFromFilename(char *filename)
12967 SetupFileHash *setup_file_hash;
12969 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12972 // read (and overwrite with) values that may be specified in config file
12973 InitSoundSettings_FromHash(setup_file_hash, TRUE);
12975 freeSetupFileHash(setup_file_hash);
12978 void LoadSoundSettings(void)
12980 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12982 InitSoundSettings_Static();
12984 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
12986 // first look for special settings configured in level series config
12987 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
12989 if (fileExists(filename_base))
12990 LoadSoundSettingsFromFilename(filename_base);
12993 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
12995 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12996 LoadSoundSettingsFromFilename(filename_local);
12999 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13001 char *filename = getEditorSetupFilename();
13002 SetupFileList *setup_file_list, *list;
13003 SetupFileHash *element_hash;
13004 int num_unknown_tokens = 0;
13007 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13010 element_hash = newSetupFileHash();
13012 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13013 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13015 // determined size may be larger than needed (due to unknown elements)
13017 for (list = setup_file_list; list != NULL; list = list->next)
13020 // add space for up to 3 more elements for padding that may be needed
13021 *num_elements += 3;
13023 // free memory for old list of elements, if needed
13024 checked_free(*elements);
13026 // allocate memory for new list of elements
13027 *elements = checked_malloc(*num_elements * sizeof(int));
13030 for (list = setup_file_list; list != NULL; list = list->next)
13032 char *value = getHashEntry(element_hash, list->token);
13034 if (value == NULL) // try to find obsolete token mapping
13036 char *mapped_token = get_mapped_token(list->token);
13038 if (mapped_token != NULL)
13040 value = getHashEntry(element_hash, mapped_token);
13042 free(mapped_token);
13048 (*elements)[(*num_elements)++] = atoi(value);
13052 if (num_unknown_tokens == 0)
13055 Warn("unknown token(s) found in config file:");
13056 Warn("- config file: '%s'", filename);
13058 num_unknown_tokens++;
13061 Warn("- token: '%s'", list->token);
13065 if (num_unknown_tokens > 0)
13068 while (*num_elements % 4) // pad with empty elements, if needed
13069 (*elements)[(*num_elements)++] = EL_EMPTY;
13071 freeSetupFileList(setup_file_list);
13072 freeSetupFileHash(element_hash);
13075 for (i = 0; i < *num_elements; i++)
13076 Debug("editor", "element '%s' [%d]\n",
13077 element_info[(*elements)[i]].token_name, (*elements)[i]);
13081 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13084 SetupFileHash *setup_file_hash = NULL;
13085 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13086 char *filename_music, *filename_prefix, *filename_info;
13092 token_to_value_ptr[] =
13094 { "title_header", &tmp_music_file_info.title_header },
13095 { "artist_header", &tmp_music_file_info.artist_header },
13096 { "album_header", &tmp_music_file_info.album_header },
13097 { "year_header", &tmp_music_file_info.year_header },
13098 { "played_header", &tmp_music_file_info.played_header },
13100 { "title", &tmp_music_file_info.title },
13101 { "artist", &tmp_music_file_info.artist },
13102 { "album", &tmp_music_file_info.album },
13103 { "year", &tmp_music_file_info.year },
13104 { "played", &tmp_music_file_info.played },
13110 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13111 getCustomMusicFilename(basename));
13113 if (filename_music == NULL)
13116 // ---------- try to replace file extension ----------
13118 filename_prefix = getStringCopy(filename_music);
13119 if (strrchr(filename_prefix, '.') != NULL)
13120 *strrchr(filename_prefix, '.') = '\0';
13121 filename_info = getStringCat2(filename_prefix, ".txt");
13123 if (fileExists(filename_info))
13124 setup_file_hash = loadSetupFileHash(filename_info);
13126 free(filename_prefix);
13127 free(filename_info);
13129 if (setup_file_hash == NULL)
13131 // ---------- try to add file extension ----------
13133 filename_prefix = getStringCopy(filename_music);
13134 filename_info = getStringCat2(filename_prefix, ".txt");
13136 if (fileExists(filename_info))
13137 setup_file_hash = loadSetupFileHash(filename_info);
13139 free(filename_prefix);
13140 free(filename_info);
13143 if (setup_file_hash == NULL)
13146 // ---------- music file info found ----------
13148 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13150 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13152 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13154 *token_to_value_ptr[i].value_ptr =
13155 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13158 tmp_music_file_info.basename = getStringCopy(basename);
13159 tmp_music_file_info.music = music;
13160 tmp_music_file_info.is_sound = is_sound;
13162 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13163 *new_music_file_info = tmp_music_file_info;
13165 return new_music_file_info;
13168 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13170 return get_music_file_info_ext(basename, music, FALSE);
13173 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13175 return get_music_file_info_ext(basename, sound, TRUE);
13178 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13179 char *basename, boolean is_sound)
13181 for (; list != NULL; list = list->next)
13182 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13188 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13190 return music_info_listed_ext(list, basename, FALSE);
13193 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13195 return music_info_listed_ext(list, basename, TRUE);
13198 void LoadMusicInfo(void)
13200 int num_music_noconf = getMusicListSize_NoConf();
13201 int num_music = getMusicListSize();
13202 int num_sounds = getSoundListSize();
13203 struct FileInfo *music, *sound;
13204 struct MusicFileInfo *next, **new;
13208 while (music_file_info != NULL)
13210 next = music_file_info->next;
13212 checked_free(music_file_info->basename);
13214 checked_free(music_file_info->title_header);
13215 checked_free(music_file_info->artist_header);
13216 checked_free(music_file_info->album_header);
13217 checked_free(music_file_info->year_header);
13218 checked_free(music_file_info->played_header);
13220 checked_free(music_file_info->title);
13221 checked_free(music_file_info->artist);
13222 checked_free(music_file_info->album);
13223 checked_free(music_file_info->year);
13224 checked_free(music_file_info->played);
13226 free(music_file_info);
13228 music_file_info = next;
13231 new = &music_file_info;
13233 // get (configured or unconfigured) music file info for all levels
13234 for (i = leveldir_current->first_level;
13235 i <= leveldir_current->last_level; i++)
13239 if (levelset.music[i] != MUS_UNDEFINED)
13241 // get music file info for configured level music
13242 music_nr = levelset.music[i];
13244 else if (num_music_noconf > 0)
13246 // get music file info for unconfigured level music
13247 int level_pos = i - leveldir_current->first_level;
13249 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13256 char *basename = getMusicInfoEntryFilename(music_nr);
13258 if (basename == NULL)
13261 if (!music_info_listed(music_file_info, basename))
13263 *new = get_music_file_info(basename, music_nr);
13266 new = &(*new)->next;
13270 // get music file info for all remaining configured music files
13271 for (i = 0; i < num_music; i++)
13273 music = getMusicListEntry(i);
13275 if (music->filename == NULL)
13278 if (strEqual(music->filename, UNDEFINED_FILENAME))
13281 // a configured file may be not recognized as music
13282 if (!FileIsMusic(music->filename))
13285 if (!music_info_listed(music_file_info, music->filename))
13287 *new = get_music_file_info(music->filename, i);
13290 new = &(*new)->next;
13294 // get sound file info for all configured sound files
13295 for (i = 0; i < num_sounds; i++)
13297 sound = getSoundListEntry(i);
13299 if (sound->filename == NULL)
13302 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13305 // a configured file may be not recognized as sound
13306 if (!FileIsSound(sound->filename))
13309 if (!sound_info_listed(music_file_info, sound->filename))
13311 *new = get_sound_file_info(sound->filename, i);
13313 new = &(*new)->next;
13317 // add pointers to previous list nodes
13319 struct MusicFileInfo *node = music_file_info;
13321 while (node != NULL)
13324 node->next->prev = node;
13330 static void add_helpanim_entry(int element, int action, int direction,
13331 int delay, int *num_list_entries)
13333 struct HelpAnimInfo *new_list_entry;
13334 (*num_list_entries)++;
13337 checked_realloc(helpanim_info,
13338 *num_list_entries * sizeof(struct HelpAnimInfo));
13339 new_list_entry = &helpanim_info[*num_list_entries - 1];
13341 new_list_entry->element = element;
13342 new_list_entry->action = action;
13343 new_list_entry->direction = direction;
13344 new_list_entry->delay = delay;
13347 static void print_unknown_token(char *filename, char *token, int token_nr)
13352 Warn("unknown token(s) found in config file:");
13353 Warn("- config file: '%s'", filename);
13356 Warn("- token: '%s'", token);
13359 static void print_unknown_token_end(int token_nr)
13365 void LoadHelpAnimInfo(void)
13367 char *filename = getHelpAnimFilename();
13368 SetupFileList *setup_file_list = NULL, *list;
13369 SetupFileHash *element_hash, *action_hash, *direction_hash;
13370 int num_list_entries = 0;
13371 int num_unknown_tokens = 0;
13374 if (fileExists(filename))
13375 setup_file_list = loadSetupFileList(filename);
13377 if (setup_file_list == NULL)
13379 // use reliable default values from static configuration
13380 SetupFileList *insert_ptr;
13382 insert_ptr = setup_file_list =
13383 newSetupFileList(helpanim_config[0].token,
13384 helpanim_config[0].value);
13386 for (i = 1; helpanim_config[i].token; i++)
13387 insert_ptr = addListEntry(insert_ptr,
13388 helpanim_config[i].token,
13389 helpanim_config[i].value);
13392 element_hash = newSetupFileHash();
13393 action_hash = newSetupFileHash();
13394 direction_hash = newSetupFileHash();
13396 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13397 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13399 for (i = 0; i < NUM_ACTIONS; i++)
13400 setHashEntry(action_hash, element_action_info[i].suffix,
13401 i_to_a(element_action_info[i].value));
13403 // do not store direction index (bit) here, but direction value!
13404 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13405 setHashEntry(direction_hash, element_direction_info[i].suffix,
13406 i_to_a(1 << element_direction_info[i].value));
13408 for (list = setup_file_list; list != NULL; list = list->next)
13410 char *element_token, *action_token, *direction_token;
13411 char *element_value, *action_value, *direction_value;
13412 int delay = atoi(list->value);
13414 if (strEqual(list->token, "end"))
13416 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13421 /* first try to break element into element/action/direction parts;
13422 if this does not work, also accept combined "element[.act][.dir]"
13423 elements (like "dynamite.active"), which are unique elements */
13425 if (strchr(list->token, '.') == NULL) // token contains no '.'
13427 element_value = getHashEntry(element_hash, list->token);
13428 if (element_value != NULL) // element found
13429 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13430 &num_list_entries);
13433 // no further suffixes found -- this is not an element
13434 print_unknown_token(filename, list->token, num_unknown_tokens++);
13440 // token has format "<prefix>.<something>"
13442 action_token = strchr(list->token, '.'); // suffix may be action ...
13443 direction_token = action_token; // ... or direction
13445 element_token = getStringCopy(list->token);
13446 *strchr(element_token, '.') = '\0';
13448 element_value = getHashEntry(element_hash, element_token);
13450 if (element_value == NULL) // this is no element
13452 element_value = getHashEntry(element_hash, list->token);
13453 if (element_value != NULL) // combined element found
13454 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13455 &num_list_entries);
13457 print_unknown_token(filename, list->token, num_unknown_tokens++);
13459 free(element_token);
13464 action_value = getHashEntry(action_hash, action_token);
13466 if (action_value != NULL) // action found
13468 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13469 &num_list_entries);
13471 free(element_token);
13476 direction_value = getHashEntry(direction_hash, direction_token);
13478 if (direction_value != NULL) // direction found
13480 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13481 &num_list_entries);
13483 free(element_token);
13488 if (strchr(action_token + 1, '.') == NULL)
13490 // no further suffixes found -- this is not an action nor direction
13492 element_value = getHashEntry(element_hash, list->token);
13493 if (element_value != NULL) // combined element found
13494 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13495 &num_list_entries);
13497 print_unknown_token(filename, list->token, num_unknown_tokens++);
13499 free(element_token);
13504 // token has format "<prefix>.<suffix>.<something>"
13506 direction_token = strchr(action_token + 1, '.');
13508 action_token = getStringCopy(action_token);
13509 *strchr(action_token + 1, '.') = '\0';
13511 action_value = getHashEntry(action_hash, action_token);
13513 if (action_value == NULL) // this is no action
13515 element_value = getHashEntry(element_hash, list->token);
13516 if (element_value != NULL) // combined element found
13517 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13518 &num_list_entries);
13520 print_unknown_token(filename, list->token, num_unknown_tokens++);
13522 free(element_token);
13523 free(action_token);
13528 direction_value = getHashEntry(direction_hash, direction_token);
13530 if (direction_value != NULL) // direction found
13532 add_helpanim_entry(atoi(element_value), atoi(action_value),
13533 atoi(direction_value), delay, &num_list_entries);
13535 free(element_token);
13536 free(action_token);
13541 // this is no direction
13543 element_value = getHashEntry(element_hash, list->token);
13544 if (element_value != NULL) // combined element found
13545 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13546 &num_list_entries);
13548 print_unknown_token(filename, list->token, num_unknown_tokens++);
13550 free(element_token);
13551 free(action_token);
13554 print_unknown_token_end(num_unknown_tokens);
13556 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13557 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13559 freeSetupFileList(setup_file_list);
13560 freeSetupFileHash(element_hash);
13561 freeSetupFileHash(action_hash);
13562 freeSetupFileHash(direction_hash);
13565 for (i = 0; i < num_list_entries; i++)
13566 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13567 EL_NAME(helpanim_info[i].element),
13568 helpanim_info[i].element,
13569 helpanim_info[i].action,
13570 helpanim_info[i].direction,
13571 helpanim_info[i].delay);
13575 void LoadHelpTextInfo(void)
13577 char *filename = getHelpTextFilename();
13580 if (helptext_info != NULL)
13582 freeSetupFileHash(helptext_info);
13583 helptext_info = NULL;
13586 if (fileExists(filename))
13587 helptext_info = loadSetupFileHash(filename);
13589 if (helptext_info == NULL)
13591 // use reliable default values from static configuration
13592 helptext_info = newSetupFileHash();
13594 for (i = 0; helptext_config[i].token; i++)
13595 setHashEntry(helptext_info,
13596 helptext_config[i].token,
13597 helptext_config[i].value);
13601 BEGIN_HASH_ITERATION(helptext_info, itr)
13603 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13604 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13606 END_HASH_ITERATION(hash, itr)
13611 // ----------------------------------------------------------------------------
13613 // ----------------------------------------------------------------------------
13615 #define MAX_NUM_CONVERT_LEVELS 1000
13617 void ConvertLevels(void)
13619 static LevelDirTree *convert_leveldir = NULL;
13620 static int convert_level_nr = -1;
13621 static int num_levels_handled = 0;
13622 static int num_levels_converted = 0;
13623 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13626 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13627 global.convert_leveldir);
13629 if (convert_leveldir == NULL)
13630 Fail("no such level identifier: '%s'", global.convert_leveldir);
13632 leveldir_current = convert_leveldir;
13634 if (global.convert_level_nr != -1)
13636 convert_leveldir->first_level = global.convert_level_nr;
13637 convert_leveldir->last_level = global.convert_level_nr;
13640 convert_level_nr = convert_leveldir->first_level;
13642 PrintLine("=", 79);
13643 Print("Converting levels\n");
13644 PrintLine("-", 79);
13645 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13646 Print("Level series name: '%s'\n", convert_leveldir->name);
13647 Print("Level series author: '%s'\n", convert_leveldir->author);
13648 Print("Number of levels: %d\n", convert_leveldir->levels);
13649 PrintLine("=", 79);
13652 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13653 levels_failed[i] = FALSE;
13655 while (convert_level_nr <= convert_leveldir->last_level)
13657 char *level_filename;
13660 level_nr = convert_level_nr++;
13662 Print("Level %03d: ", level_nr);
13664 LoadLevel(level_nr);
13665 if (level.no_level_file || level.no_valid_file)
13667 Print("(no level)\n");
13671 Print("converting level ... ");
13674 // special case: conversion of some EMC levels as requested by ACME
13675 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13678 level_filename = getDefaultLevelFilename(level_nr);
13679 new_level = !fileExists(level_filename);
13683 SaveLevel(level_nr);
13685 num_levels_converted++;
13687 Print("converted.\n");
13691 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13692 levels_failed[level_nr] = TRUE;
13694 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13697 num_levels_handled++;
13701 PrintLine("=", 79);
13702 Print("Number of levels handled: %d\n", num_levels_handled);
13703 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13704 (num_levels_handled ?
13705 num_levels_converted * 100 / num_levels_handled : 0));
13706 PrintLine("-", 79);
13707 Print("Summary (for automatic parsing by scripts):\n");
13708 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13709 convert_leveldir->identifier, num_levels_converted,
13710 num_levels_handled,
13711 (num_levels_handled ?
13712 num_levels_converted * 100 / num_levels_handled : 0));
13714 if (num_levels_handled != num_levels_converted)
13716 Print(", FAILED:");
13717 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13718 if (levels_failed[i])
13723 PrintLine("=", 79);
13725 CloseAllAndExit(0);
13729 // ----------------------------------------------------------------------------
13730 // create and save images for use in level sketches (raw BMP format)
13731 // ----------------------------------------------------------------------------
13733 void CreateLevelSketchImages(void)
13739 InitElementPropertiesGfxElement();
13741 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13742 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13744 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13746 int element = getMappedElement(i);
13747 char basename1[16];
13748 char basename2[16];
13752 sprintf(basename1, "%04d.bmp", i);
13753 sprintf(basename2, "%04ds.bmp", i);
13755 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13756 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13758 DrawSizedElement(0, 0, element, TILESIZE);
13759 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13761 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13762 Fail("cannot save level sketch image file '%s'", filename1);
13764 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13765 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13767 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13768 Fail("cannot save level sketch image file '%s'", filename2);
13773 // create corresponding SQL statements (for normal and small images)
13776 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13777 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13780 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13781 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13783 // optional: create content for forum level sketch demonstration post
13785 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13788 FreeBitmap(bitmap1);
13789 FreeBitmap(bitmap2);
13792 fprintf(stderr, "\n");
13794 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13796 CloseAllAndExit(0);
13800 // ----------------------------------------------------------------------------
13801 // create and save images for element collecting animations (raw BMP format)
13802 // ----------------------------------------------------------------------------
13804 static boolean createCollectImage(int element)
13806 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13809 void CreateCollectElementImages(void)
13813 int anim_frames = num_steps - 1;
13814 int tile_size = TILESIZE;
13815 int anim_width = tile_size * anim_frames;
13816 int anim_height = tile_size;
13817 int num_collect_images = 0;
13818 int pos_collect_images = 0;
13820 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13821 if (createCollectImage(i))
13822 num_collect_images++;
13824 Info("Creating %d element collecting animation images ...",
13825 num_collect_images);
13827 int dst_width = anim_width * 2;
13828 int dst_height = anim_height * num_collect_images / 2;
13829 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13830 char *basename_bmp = "RocksCollect.bmp";
13831 char *basename_png = "RocksCollect.png";
13832 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13833 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13834 int len_filename_bmp = strlen(filename_bmp);
13835 int len_filename_png = strlen(filename_png);
13836 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13837 char cmd_convert[max_command_len];
13839 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13843 // force using RGBA surface for destination bitmap
13844 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13845 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13847 dst_bitmap->surface =
13848 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13850 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13852 if (!createCollectImage(i))
13855 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13856 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13857 int graphic = el2img(i);
13858 char *token_name = element_info[i].token_name;
13859 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13860 Bitmap *src_bitmap;
13863 Info("- creating collecting image for '%s' ...", token_name);
13865 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13867 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13868 tile_size, tile_size, 0, 0);
13870 // force using RGBA surface for temporary bitmap (using transparent black)
13871 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13872 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13874 tmp_bitmap->surface =
13875 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13877 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13879 for (j = 0; j < anim_frames; j++)
13881 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13882 int frame_size = frame_size_final * num_steps;
13883 int offset = (tile_size - frame_size_final) / 2;
13884 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13886 while (frame_size > frame_size_final)
13890 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13892 FreeBitmap(frame_bitmap);
13894 frame_bitmap = half_bitmap;
13897 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13898 frame_size_final, frame_size_final,
13899 dst_x + j * tile_size + offset, dst_y + offset);
13901 FreeBitmap(frame_bitmap);
13904 tmp_bitmap->surface_masked = NULL;
13906 FreeBitmap(tmp_bitmap);
13908 pos_collect_images++;
13911 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13912 Fail("cannot save element collecting image file '%s'", filename_bmp);
13914 FreeBitmap(dst_bitmap);
13916 Info("Converting image file from BMP to PNG ...");
13918 if (system(cmd_convert) != 0)
13919 Fail("converting image file failed");
13921 unlink(filename_bmp);
13925 CloseAllAndExit(0);
13929 // ----------------------------------------------------------------------------
13930 // create and save images for custom and group elements (raw BMP format)
13931 // ----------------------------------------------------------------------------
13933 void CreateCustomElementImages(char *directory)
13935 char *src_basename = "RocksCE-template.ilbm";
13936 char *dst_basename = "RocksCE.bmp";
13937 char *src_filename = getPath2(directory, src_basename);
13938 char *dst_filename = getPath2(directory, dst_basename);
13939 Bitmap *src_bitmap;
13941 int yoffset_ce = 0;
13942 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13945 InitVideoDefaults();
13947 ReCreateBitmap(&backbuffer, video.width, video.height);
13949 src_bitmap = LoadImage(src_filename);
13951 bitmap = CreateBitmap(TILEX * 16 * 2,
13952 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13955 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13962 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13963 TILEX * x, TILEY * y + yoffset_ce);
13965 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13967 TILEX * x + TILEX * 16,
13968 TILEY * y + yoffset_ce);
13970 for (j = 2; j >= 0; j--)
13974 BlitBitmap(src_bitmap, bitmap,
13975 TILEX + c * 7, 0, 6, 10,
13976 TILEX * x + 6 + j * 7,
13977 TILEY * y + 11 + yoffset_ce);
13979 BlitBitmap(src_bitmap, bitmap,
13980 TILEX + c * 8, TILEY, 6, 10,
13981 TILEX * 16 + TILEX * x + 6 + j * 8,
13982 TILEY * y + 10 + yoffset_ce);
13988 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13995 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13996 TILEX * x, TILEY * y + yoffset_ge);
13998 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14000 TILEX * x + TILEX * 16,
14001 TILEY * y + yoffset_ge);
14003 for (j = 1; j >= 0; j--)
14007 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14008 TILEX * x + 6 + j * 10,
14009 TILEY * y + 11 + yoffset_ge);
14011 BlitBitmap(src_bitmap, bitmap,
14012 TILEX + c * 8, TILEY + 12, 6, 10,
14013 TILEX * 16 + TILEX * x + 10 + j * 8,
14014 TILEY * y + 10 + yoffset_ge);
14020 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14021 Fail("cannot save CE graphics file '%s'", dst_filename);
14023 FreeBitmap(bitmap);
14025 CloseAllAndExit(0);