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"
26 #define ENABLE_UNUSED_CODE 0 // currently unused functions
27 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
28 #define ENABLE_RESERVED_CODE 0 // reserved for later use
30 #define CHUNK_ID_LEN 4 // IFF style chunk id length
31 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
32 #define CHUNK_SIZE_NONE -1 // do not write chunk size
34 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
37 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
38 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
39 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
40 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
41 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
42 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
43 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
44 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
45 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
46 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
47 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
49 // (element number, number of change pages, change page number)
50 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
52 // (element number only)
53 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
54 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
56 // (nothing at all if unchanged)
57 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
59 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
60 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
61 #define TAPE_CHUNK_HEAD_UNUSED 1 // unused tape header bytes
62 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
64 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
65 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
66 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
68 // file identifier strings
69 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
70 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
71 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
73 // values for deciding when (not) to save configuration data
74 #define SAVE_CONF_NEVER 0
75 #define SAVE_CONF_ALWAYS 1
76 #define SAVE_CONF_WHEN_CHANGED -1
78 // values for chunks using micro chunks
79 #define CONF_MASK_1_BYTE 0x00
80 #define CONF_MASK_2_BYTE 0x40
81 #define CONF_MASK_4_BYTE 0x80
82 #define CONF_MASK_MULTI_BYTES 0xc0
84 #define CONF_MASK_BYTES 0xc0
85 #define CONF_MASK_TOKEN 0x3f
87 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
88 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
89 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
90 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
92 // these definitions are just for convenience of use and readability
93 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
94 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
95 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
96 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
98 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
99 (x) == CONF_MASK_2_BYTE ? 2 : \
100 (x) == CONF_MASK_4_BYTE ? 4 : 0)
102 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
103 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
104 #define CONF_ELEMENT_NUM_BYTES (2)
106 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
107 (t) == TYPE_ELEMENT_LIST ? \
108 CONF_ELEMENT_NUM_BYTES : \
109 (t) == TYPE_CONTENT || \
110 (t) == TYPE_CONTENT_LIST ? \
111 CONF_CONTENT_NUM_BYTES : 1)
113 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
114 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
115 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
117 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
119 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
120 CONF_ELEMENT_NUM_BYTES)
121 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
122 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
124 // temporary variables used to store pointers to structure members
125 static struct LevelInfo li;
126 static struct ElementInfo xx_ei, yy_ei;
127 static struct ElementChangeInfo xx_change;
128 static struct ElementGroupInfo xx_group;
129 static struct EnvelopeInfo xx_envelope;
130 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
131 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
132 static int xx_num_contents;
133 static int xx_current_change_page;
134 static char xx_default_string_empty[1] = "";
135 static int xx_string_length_unused;
137 struct LevelFileConfigInfo
139 int element; // element for which data is to be stored
140 int save_type; // save data always, never or when changed
141 int data_type; // data type (used internally, not stored)
142 int conf_type; // micro chunk identifier (stored in file)
145 void *value; // variable that holds the data to be stored
146 int default_value; // initial default value for this variable
149 void *value_copy; // variable that holds the data to be copied
150 void *num_entities; // number of entities for multi-byte data
151 int default_num_entities; // default number of entities for this data
152 int max_num_entities; // maximal number of entities for this data
153 char *default_string; // optional default string for string data
156 static struct LevelFileConfigInfo chunk_config_INFO[] =
158 // ---------- values not related to single elements -------------------------
161 -1, SAVE_CONF_ALWAYS,
162 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
163 &li.game_engine_type, GAME_ENGINE_TYPE_RND
167 -1, SAVE_CONF_ALWAYS,
168 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
169 &li.fieldx, STD_LEV_FIELDX
172 -1, SAVE_CONF_ALWAYS,
173 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
174 &li.fieldy, STD_LEV_FIELDY
178 -1, SAVE_CONF_ALWAYS,
179 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
191 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
197 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
198 &li.use_step_counter, FALSE
203 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
204 &li.wind_direction_initial, MV_NONE
209 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
210 &li.em_slippery_gems, FALSE
215 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
216 &li.use_custom_template, FALSE
221 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
222 &li.can_move_into_acid_bits, ~0 // default: everything can
227 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
228 &li.dont_collide_with_bits, ~0 // default: always deadly
233 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
234 &li.em_explodes_by_fire, FALSE
239 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
240 &li.score[SC_TIME_BONUS], 1
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
246 &li.auto_exit_sokoban, FALSE
251 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
252 &li.auto_count_gems, FALSE
257 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
258 &li.solved_by_one_player, FALSE
263 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
264 &li.time_score_base, 1
274 static struct LevelFileConfigInfo chunk_config_ELEM[] =
276 // (these values are the same for each player)
279 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
280 &li.block_last_field, FALSE // default case for EM levels
284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
285 &li.sp_block_last_field, TRUE // default case for SP levels
289 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
290 &li.instant_relocation, FALSE
294 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
295 &li.can_pass_to_walkable, FALSE
299 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
300 &li.block_snap_field, TRUE
304 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
305 &li.continuous_snapping, TRUE
309 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
310 &li.shifted_relocation, FALSE
314 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
315 &li.lazy_relocation, FALSE
319 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
320 &li.finish_dig_collect, TRUE
323 // (these values are different for each player)
326 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
327 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
332 &li.initial_player_gravity[0], FALSE
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
337 &li.use_start_element[0], FALSE
341 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
342 &li.start_element[0], EL_PLAYER_1
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
347 &li.use_artwork_element[0], FALSE
351 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
352 &li.artwork_element[0], EL_PLAYER_1
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
357 &li.use_explosion_element[0], FALSE
361 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
362 &li.explosion_element[0], EL_PLAYER_1
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
367 &li.use_initial_inventory[0], FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
372 &li.initial_inventory_size[0], 1
376 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
377 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
378 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[1], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[1], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[1], EL_PLAYER_2
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[1], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[1], EL_PLAYER_2
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[1], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[1], EL_PLAYER_2
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[1], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[1], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[2], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[2], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[2], EL_PLAYER_3
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[2], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[2], EL_PLAYER_3
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[2], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[2], EL_PLAYER_3
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[2], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[2], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[3], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[3], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[3], EL_PLAYER_4
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[3], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[3], EL_PLAYER_4
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[3], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[3], EL_PLAYER_4
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[3], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[3], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
555 &li.score[SC_EMERALD], 10
560 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
561 &li.score[SC_DIAMOND], 10
566 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
567 &li.score[SC_BUG], 10
572 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
573 &li.score[SC_SPACESHIP], 10
578 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
579 &li.score[SC_PACMAN], 10
584 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
585 &li.score[SC_NUT], 10
590 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
591 &li.score[SC_DYNAMITE], 10
596 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
597 &li.score[SC_KEY], 10
602 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
603 &li.score[SC_PEARL], 10
608 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
609 &li.score[SC_CRYSTAL], 10
614 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
615 &li.amoeba_content, EL_DIAMOND
619 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
624 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
625 &li.grow_into_diggable, TRUE
630 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
631 &li.yamyam_content, EL_ROCK, NULL,
632 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
636 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
637 &li.score[SC_YAMYAM], 10
642 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
643 &li.score[SC_ROBOT], 10
647 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
653 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
659 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
660 &li.time_magic_wall, 10
665 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
666 &li.game_of_life[0], 2
670 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
671 &li.game_of_life[1], 3
675 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
676 &li.game_of_life[2], 3
680 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
681 &li.game_of_life[3], 3
685 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
686 &li.use_life_bugs, FALSE
691 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
696 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
701 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
706 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
711 EL_TIMEGATE_SWITCH, -1,
712 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
713 &li.time_timegate, 10
717 EL_LIGHT_SWITCH_ACTIVE, -1,
718 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
723 EL_SHIELD_NORMAL, -1,
724 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
725 &li.shield_normal_time, 10
728 EL_SHIELD_NORMAL, -1,
729 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
730 &li.score[SC_SHIELD], 10
734 EL_SHIELD_DEADLY, -1,
735 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
736 &li.shield_deadly_time, 10
739 EL_SHIELD_DEADLY, -1,
740 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
741 &li.score[SC_SHIELD], 10
746 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
751 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
752 &li.extra_time_score, 10
756 EL_TIME_ORB_FULL, -1,
757 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
758 &li.time_orb_time, 10
761 EL_TIME_ORB_FULL, -1,
762 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
763 &li.use_time_orb_bug, FALSE
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
769 &li.use_spring_bug, FALSE
774 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
775 &li.android_move_time, 10
779 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
780 &li.android_clone_time, 10
783 EL_EMC_ANDROID, SAVE_CONF_NEVER,
784 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
785 &li.android_clone_element[0], EL_EMPTY, NULL,
786 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
790 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
791 &li.android_clone_element[0], EL_EMPTY, NULL,
792 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
797 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
802 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
807 EL_EMC_MAGNIFIER, -1,
808 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
809 &li.magnify_score, 10
812 EL_EMC_MAGNIFIER, -1,
813 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
818 EL_EMC_MAGIC_BALL, -1,
819 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
823 EL_EMC_MAGIC_BALL, -1,
824 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
825 &li.ball_random, FALSE
828 EL_EMC_MAGIC_BALL, -1,
829 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
830 &li.ball_active_initial, FALSE
833 EL_EMC_MAGIC_BALL, -1,
834 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
835 &li.ball_content, EL_EMPTY, NULL,
836 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
840 EL_SOKOBAN_FIELD_EMPTY, -1,
841 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
842 &li.sb_fields_needed, TRUE
846 EL_SOKOBAN_OBJECT, -1,
847 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
848 &li.sb_objects_needed, TRUE
853 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
854 &li.mm_laser_red, FALSE
858 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
859 &li.mm_laser_green, FALSE
863 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
864 &li.mm_laser_blue, TRUE
869 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
870 &li.df_laser_red, TRUE
874 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
875 &li.df_laser_green, TRUE
879 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
880 &li.df_laser_blue, FALSE
884 EL_MM_FUSE_ACTIVE, -1,
885 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
890 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
895 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
899 EL_MM_STEEL_BLOCK, -1,
900 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
901 &li.mm_time_block, 75
905 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
906 &li.score[SC_ELEM_BONUS], 10
909 // ---------- unused values -------------------------------------------------
912 EL_UNKNOWN, SAVE_CONF_NEVER,
913 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
914 &li.score[SC_UNKNOWN_15], 10
924 static struct LevelFileConfigInfo chunk_config_NOTE[] =
928 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
929 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
933 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
934 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
939 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
940 &xx_envelope.autowrap, FALSE
944 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
945 &xx_envelope.centered, FALSE
950 TYPE_STRING, CONF_VALUE_BYTES(1),
951 &xx_envelope.text, -1, NULL,
952 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
953 &xx_default_string_empty[0]
963 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
967 TYPE_STRING, CONF_VALUE_BYTES(1),
968 &xx_ei.description[0], -1,
969 &yy_ei.description[0],
970 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
971 &xx_default_description[0]
976 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
977 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
978 &yy_ei.properties[EP_BITFIELD_BASE_NR]
980 #if ENABLE_RESERVED_CODE
981 // (reserved for later use)
984 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
985 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
986 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
992 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
993 &xx_ei.use_gfx_element, FALSE,
994 &yy_ei.use_gfx_element
998 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
999 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1000 &yy_ei.gfx_element_initial
1005 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1006 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1007 &yy_ei.access_direction
1012 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1013 &xx_ei.collect_score_initial, 10,
1014 &yy_ei.collect_score_initial
1018 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1019 &xx_ei.collect_count_initial, 1,
1020 &yy_ei.collect_count_initial
1025 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1026 &xx_ei.ce_value_fixed_initial, 0,
1027 &yy_ei.ce_value_fixed_initial
1031 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1032 &xx_ei.ce_value_random_initial, 0,
1033 &yy_ei.ce_value_random_initial
1037 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1038 &xx_ei.use_last_ce_value, FALSE,
1039 &yy_ei.use_last_ce_value
1044 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1045 &xx_ei.push_delay_fixed, 8,
1046 &yy_ei.push_delay_fixed
1050 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1051 &xx_ei.push_delay_random, 8,
1052 &yy_ei.push_delay_random
1056 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1057 &xx_ei.drop_delay_fixed, 0,
1058 &yy_ei.drop_delay_fixed
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1063 &xx_ei.drop_delay_random, 0,
1064 &yy_ei.drop_delay_random
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1069 &xx_ei.move_delay_fixed, 0,
1070 &yy_ei.move_delay_fixed
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1075 &xx_ei.move_delay_random, 0,
1076 &yy_ei.move_delay_random
1081 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1082 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1087 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1088 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1089 &yy_ei.move_direction_initial
1093 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1094 &xx_ei.move_stepsize, TILEX / 8,
1095 &yy_ei.move_stepsize
1100 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1101 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1102 &yy_ei.move_enter_element
1106 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1107 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1108 &yy_ei.move_leave_element
1112 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1113 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1114 &yy_ei.move_leave_type
1119 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1120 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1121 &yy_ei.slippery_type
1126 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1127 &xx_ei.explosion_type, EXPLODES_3X3,
1128 &yy_ei.explosion_type
1132 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1133 &xx_ei.explosion_delay, 16,
1134 &yy_ei.explosion_delay
1138 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1139 &xx_ei.ignition_delay, 8,
1140 &yy_ei.ignition_delay
1145 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1146 &xx_ei.content, EL_EMPTY_SPACE,
1148 &xx_num_contents, 1, 1
1151 // ---------- "num_change_pages" must be the last entry ---------------------
1154 -1, SAVE_CONF_ALWAYS,
1155 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1156 &xx_ei.num_change_pages, 1,
1157 &yy_ei.num_change_pages
1168 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1170 // ---------- "current_change_page" must be the first entry -----------------
1173 -1, SAVE_CONF_ALWAYS,
1174 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1175 &xx_current_change_page, -1
1178 // ---------- (the remaining entries can be in any order) -------------------
1182 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1183 &xx_change.can_change, FALSE
1188 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1189 &xx_event_bits[0], 0
1193 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1194 &xx_event_bits[1], 0
1199 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1200 &xx_change.trigger_player, CH_PLAYER_ANY
1204 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1205 &xx_change.trigger_side, CH_SIDE_ANY
1209 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1210 &xx_change.trigger_page, CH_PAGE_ANY
1215 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1216 &xx_change.target_element, EL_EMPTY_SPACE
1221 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1222 &xx_change.delay_fixed, 0
1226 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1227 &xx_change.delay_random, 0
1231 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1232 &xx_change.delay_frames, FRAMES_PER_SECOND
1237 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1238 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1243 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1244 &xx_change.explode, FALSE
1248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1249 &xx_change.use_target_content, FALSE
1253 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1254 &xx_change.only_if_complete, FALSE
1258 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1259 &xx_change.use_random_replace, FALSE
1263 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1264 &xx_change.random_percentage, 100
1268 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1269 &xx_change.replace_when, CP_WHEN_EMPTY
1274 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1275 &xx_change.has_action, FALSE
1279 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1280 &xx_change.action_type, CA_NO_ACTION
1284 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1285 &xx_change.action_mode, CA_MODE_UNDEFINED
1289 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1290 &xx_change.action_arg, CA_ARG_UNDEFINED
1295 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1296 &xx_change.action_element, EL_EMPTY_SPACE
1301 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1302 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1303 &xx_num_contents, 1, 1
1313 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1317 TYPE_STRING, CONF_VALUE_BYTES(1),
1318 &xx_ei.description[0], -1, NULL,
1319 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1320 &xx_default_description[0]
1325 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1326 &xx_ei.use_gfx_element, FALSE
1330 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1331 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1336 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1337 &xx_group.choice_mode, ANIM_RANDOM
1342 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1343 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1344 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1354 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1358 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1359 &li.block_snap_field, TRUE
1363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1364 &li.continuous_snapping, TRUE
1368 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1369 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1373 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1374 &li.use_start_element[0], FALSE
1378 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1379 &li.start_element[0], EL_PLAYER_1
1383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1384 &li.use_artwork_element[0], FALSE
1388 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1389 &li.artwork_element[0], EL_PLAYER_1
1393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1394 &li.use_explosion_element[0], FALSE
1398 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1399 &li.explosion_element[0], EL_PLAYER_1
1414 filetype_id_list[] =
1416 { LEVEL_FILE_TYPE_RND, "RND" },
1417 { LEVEL_FILE_TYPE_BD, "BD" },
1418 { LEVEL_FILE_TYPE_EM, "EM" },
1419 { LEVEL_FILE_TYPE_SP, "SP" },
1420 { LEVEL_FILE_TYPE_DX, "DX" },
1421 { LEVEL_FILE_TYPE_SB, "SB" },
1422 { LEVEL_FILE_TYPE_DC, "DC" },
1423 { LEVEL_FILE_TYPE_MM, "MM" },
1424 { LEVEL_FILE_TYPE_MM, "DF" },
1429 // ============================================================================
1430 // level file functions
1431 // ============================================================================
1433 static boolean check_special_flags(char *flag)
1435 if (strEqual(options.special_flags, flag) ||
1436 strEqual(leveldir_current->special_flags, flag))
1442 static struct DateInfo getCurrentDate(void)
1444 time_t epoch_seconds = time(NULL);
1445 struct tm *now = localtime(&epoch_seconds);
1446 struct DateInfo date;
1448 date.year = now->tm_year + 1900;
1449 date.month = now->tm_mon + 1;
1450 date.day = now->tm_mday;
1452 date.src = DATE_SRC_CLOCK;
1457 static void resetEventFlags(struct ElementChangeInfo *change)
1461 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1462 change->has_event[i] = FALSE;
1465 static void resetEventBits(void)
1469 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1470 xx_event_bits[i] = 0;
1473 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1477 /* important: only change event flag if corresponding event bit is set
1478 (this is because all xx_event_bits[] values are loaded separately,
1479 and all xx_event_bits[] values are set back to zero before loading
1480 another value xx_event_bits[x] (each value representing 32 flags)) */
1482 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1483 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1484 change->has_event[i] = TRUE;
1487 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1491 /* in contrast to the above function setEventFlagsFromEventBits(), it
1492 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1493 depending on the corresponding change->has_event[i] values here, as
1494 all xx_event_bits[] values are reset in resetEventBits() before */
1496 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1497 if (change->has_event[i])
1498 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1501 static char *getDefaultElementDescription(struct ElementInfo *ei)
1503 static char description[MAX_ELEMENT_NAME_LEN + 1];
1504 char *default_description = (ei->custom_description != NULL ?
1505 ei->custom_description :
1506 ei->editor_description);
1509 // always start with reliable default values
1510 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1511 description[i] = '\0';
1513 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1514 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1516 return &description[0];
1519 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1521 char *default_description = getDefaultElementDescription(ei);
1524 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1525 ei->description[i] = default_description[i];
1528 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1532 for (i = 0; conf[i].data_type != -1; i++)
1534 int default_value = conf[i].default_value;
1535 int data_type = conf[i].data_type;
1536 int conf_type = conf[i].conf_type;
1537 int byte_mask = conf_type & CONF_MASK_BYTES;
1539 if (byte_mask == CONF_MASK_MULTI_BYTES)
1541 int default_num_entities = conf[i].default_num_entities;
1542 int max_num_entities = conf[i].max_num_entities;
1544 *(int *)(conf[i].num_entities) = default_num_entities;
1546 if (data_type == TYPE_STRING)
1548 char *default_string = conf[i].default_string;
1549 char *string = (char *)(conf[i].value);
1551 strncpy(string, default_string, max_num_entities);
1553 else if (data_type == TYPE_ELEMENT_LIST)
1555 int *element_array = (int *)(conf[i].value);
1558 for (j = 0; j < max_num_entities; j++)
1559 element_array[j] = default_value;
1561 else if (data_type == TYPE_CONTENT_LIST)
1563 struct Content *content = (struct Content *)(conf[i].value);
1566 for (c = 0; c < max_num_entities; c++)
1567 for (y = 0; y < 3; y++)
1568 for (x = 0; x < 3; x++)
1569 content[c].e[x][y] = default_value;
1572 else // constant size configuration data (1, 2 or 4 bytes)
1574 if (data_type == TYPE_BOOLEAN)
1575 *(boolean *)(conf[i].value) = default_value;
1577 *(int *) (conf[i].value) = default_value;
1582 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1586 for (i = 0; conf[i].data_type != -1; i++)
1588 int data_type = conf[i].data_type;
1589 int conf_type = conf[i].conf_type;
1590 int byte_mask = conf_type & CONF_MASK_BYTES;
1592 if (byte_mask == CONF_MASK_MULTI_BYTES)
1594 int max_num_entities = conf[i].max_num_entities;
1596 if (data_type == TYPE_STRING)
1598 char *string = (char *)(conf[i].value);
1599 char *string_copy = (char *)(conf[i].value_copy);
1601 strncpy(string_copy, string, max_num_entities);
1603 else if (data_type == TYPE_ELEMENT_LIST)
1605 int *element_array = (int *)(conf[i].value);
1606 int *element_array_copy = (int *)(conf[i].value_copy);
1609 for (j = 0; j < max_num_entities; j++)
1610 element_array_copy[j] = element_array[j];
1612 else if (data_type == TYPE_CONTENT_LIST)
1614 struct Content *content = (struct Content *)(conf[i].value);
1615 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1618 for (c = 0; c < max_num_entities; c++)
1619 for (y = 0; y < 3; y++)
1620 for (x = 0; x < 3; x++)
1621 content_copy[c].e[x][y] = content[c].e[x][y];
1624 else // constant size configuration data (1, 2 or 4 bytes)
1626 if (data_type == TYPE_BOOLEAN)
1627 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1629 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1634 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1638 xx_ei = *ei_from; // copy element data into temporary buffer
1639 yy_ei = *ei_to; // copy element data into temporary buffer
1641 copyConfigFromConfigList(chunk_config_CUSX_base);
1646 // ---------- reinitialize and copy change pages ----------
1648 ei_to->num_change_pages = ei_from->num_change_pages;
1649 ei_to->current_change_page = ei_from->current_change_page;
1651 setElementChangePages(ei_to, ei_to->num_change_pages);
1653 for (i = 0; i < ei_to->num_change_pages; i++)
1654 ei_to->change_page[i] = ei_from->change_page[i];
1656 // ---------- copy group element info ----------
1657 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1658 *ei_to->group = *ei_from->group;
1660 // mark this custom element as modified
1661 ei_to->modified_settings = TRUE;
1664 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1666 int change_page_size = sizeof(struct ElementChangeInfo);
1668 ei->num_change_pages = MAX(1, change_pages);
1671 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1673 if (ei->current_change_page >= ei->num_change_pages)
1674 ei->current_change_page = ei->num_change_pages - 1;
1676 ei->change = &ei->change_page[ei->current_change_page];
1679 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1681 xx_change = *change; // copy change data into temporary buffer
1683 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1685 *change = xx_change;
1687 resetEventFlags(change);
1689 change->direct_action = 0;
1690 change->other_action = 0;
1692 change->pre_change_function = NULL;
1693 change->change_function = NULL;
1694 change->post_change_function = NULL;
1697 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1701 li = *level; // copy level data into temporary buffer
1702 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1703 *level = li; // copy temporary buffer back to level data
1705 setLevelInfoToDefaults_EM();
1706 setLevelInfoToDefaults_SP();
1707 setLevelInfoToDefaults_MM();
1709 level->native_em_level = &native_em_level;
1710 level->native_sp_level = &native_sp_level;
1711 level->native_mm_level = &native_mm_level;
1713 level->file_version = FILE_VERSION_ACTUAL;
1714 level->game_version = GAME_VERSION_ACTUAL;
1716 level->creation_date = getCurrentDate();
1718 level->encoding_16bit_field = TRUE;
1719 level->encoding_16bit_yamyam = TRUE;
1720 level->encoding_16bit_amoeba = TRUE;
1722 // clear level name and level author string buffers
1723 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1724 level->name[i] = '\0';
1725 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1726 level->author[i] = '\0';
1728 // set level name and level author to default values
1729 strcpy(level->name, NAMELESS_LEVEL_NAME);
1730 strcpy(level->author, ANONYMOUS_NAME);
1732 // set level playfield to playable default level with player and exit
1733 for (x = 0; x < MAX_LEV_FIELDX; x++)
1734 for (y = 0; y < MAX_LEV_FIELDY; y++)
1735 level->field[x][y] = EL_SAND;
1737 level->field[0][0] = EL_PLAYER_1;
1738 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1740 BorderElement = EL_STEELWALL;
1742 // detect custom elements when loading them
1743 level->file_has_custom_elements = FALSE;
1745 // set all bug compatibility flags to "false" => do not emulate this bug
1746 level->use_action_after_change_bug = FALSE;
1748 if (leveldir_current)
1750 // try to determine better author name than 'anonymous'
1751 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1753 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1754 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1758 switch (LEVELCLASS(leveldir_current))
1760 case LEVELCLASS_TUTORIAL:
1761 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1764 case LEVELCLASS_CONTRIB:
1765 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1766 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1769 case LEVELCLASS_PRIVATE:
1770 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1771 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1775 // keep default value
1782 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1784 static boolean clipboard_elements_initialized = FALSE;
1787 InitElementPropertiesStatic();
1789 li = *level; // copy level data into temporary buffer
1790 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1791 *level = li; // copy temporary buffer back to level data
1793 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1796 struct ElementInfo *ei = &element_info[element];
1798 // never initialize clipboard elements after the very first time
1799 // (to be able to use clipboard elements between several levels)
1800 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1803 if (IS_ENVELOPE(element))
1805 int envelope_nr = element - EL_ENVELOPE_1;
1807 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1809 level->envelope[envelope_nr] = xx_envelope;
1812 if (IS_CUSTOM_ELEMENT(element) ||
1813 IS_GROUP_ELEMENT(element) ||
1814 IS_INTERNAL_ELEMENT(element))
1816 xx_ei = *ei; // copy element data into temporary buffer
1818 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1823 setElementChangePages(ei, 1);
1824 setElementChangeInfoToDefaults(ei->change);
1826 if (IS_CUSTOM_ELEMENT(element) ||
1827 IS_GROUP_ELEMENT(element) ||
1828 IS_INTERNAL_ELEMENT(element))
1830 setElementDescriptionToDefault(ei);
1832 ei->modified_settings = FALSE;
1835 if (IS_CUSTOM_ELEMENT(element) ||
1836 IS_INTERNAL_ELEMENT(element))
1838 // internal values used in level editor
1840 ei->access_type = 0;
1841 ei->access_layer = 0;
1842 ei->access_protected = 0;
1843 ei->walk_to_action = 0;
1844 ei->smash_targets = 0;
1847 ei->can_explode_by_fire = FALSE;
1848 ei->can_explode_smashed = FALSE;
1849 ei->can_explode_impact = FALSE;
1851 ei->current_change_page = 0;
1854 if (IS_GROUP_ELEMENT(element) ||
1855 IS_INTERNAL_ELEMENT(element))
1857 struct ElementGroupInfo *group;
1859 // initialize memory for list of elements in group
1860 if (ei->group == NULL)
1861 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1865 xx_group = *group; // copy group data into temporary buffer
1867 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1873 clipboard_elements_initialized = TRUE;
1876 static void setLevelInfoToDefaults(struct LevelInfo *level,
1877 boolean level_info_only,
1878 boolean reset_file_status)
1880 setLevelInfoToDefaults_Level(level);
1882 if (!level_info_only)
1883 setLevelInfoToDefaults_Elements(level);
1885 if (reset_file_status)
1887 level->no_valid_file = FALSE;
1888 level->no_level_file = FALSE;
1891 level->changed = FALSE;
1894 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1896 level_file_info->nr = 0;
1897 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1898 level_file_info->packed = FALSE;
1900 setString(&level_file_info->basename, NULL);
1901 setString(&level_file_info->filename, NULL);
1904 int getMappedElement_SB(int, boolean);
1906 static void ActivateLevelTemplate(void)
1910 if (check_special_flags("load_xsb_to_ces"))
1912 // fill smaller playfields with padding "beyond border wall" elements
1913 if (level.fieldx < level_template.fieldx ||
1914 level.fieldy < level_template.fieldy)
1916 short field[level.fieldx][level.fieldy];
1917 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1918 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1919 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1920 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1922 // copy old playfield (which is smaller than the visible area)
1923 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1924 field[x][y] = level.field[x][y];
1926 // fill new, larger playfield with "beyond border wall" elements
1927 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1928 level.field[x][y] = getMappedElement_SB('_', TRUE);
1930 // copy the old playfield to the middle of the new playfield
1931 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1932 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1934 level.fieldx = new_fieldx;
1935 level.fieldy = new_fieldy;
1939 // Currently there is no special action needed to activate the template
1940 // data, because 'element_info' property settings overwrite the original
1941 // level data, while all other variables do not change.
1943 // Exception: 'from_level_template' elements in the original level playfield
1944 // are overwritten with the corresponding elements at the same position in
1945 // playfield from the level template.
1947 for (x = 0; x < level.fieldx; x++)
1948 for (y = 0; y < level.fieldy; y++)
1949 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1950 level.field[x][y] = level_template.field[x][y];
1952 if (check_special_flags("load_xsb_to_ces"))
1954 struct LevelInfo level_backup = level;
1956 // overwrite all individual level settings from template level settings
1957 level = level_template;
1959 // restore level file info
1960 level.file_info = level_backup.file_info;
1962 // restore playfield size
1963 level.fieldx = level_backup.fieldx;
1964 level.fieldy = level_backup.fieldy;
1966 // restore playfield content
1967 for (x = 0; x < level.fieldx; x++)
1968 for (y = 0; y < level.fieldy; y++)
1969 level.field[x][y] = level_backup.field[x][y];
1971 // restore name and author from individual level
1972 strcpy(level.name, level_backup.name);
1973 strcpy(level.author, level_backup.author);
1975 // restore flag "use_custom_template"
1976 level.use_custom_template = level_backup.use_custom_template;
1980 static char *getLevelFilenameFromBasename(char *basename)
1982 static char *filename = NULL;
1984 checked_free(filename);
1986 filename = getPath2(getCurrentLevelDir(), basename);
1991 static int getFileTypeFromBasename(char *basename)
1993 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
1995 static char *filename = NULL;
1996 struct stat file_status;
1998 // ---------- try to determine file type from filename ----------
2000 // check for typical filename of a Supaplex level package file
2001 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2002 return LEVEL_FILE_TYPE_SP;
2004 // check for typical filename of a Diamond Caves II level package file
2005 if (strSuffixLower(basename, ".dc") ||
2006 strSuffixLower(basename, ".dc2"))
2007 return LEVEL_FILE_TYPE_DC;
2009 // check for typical filename of a Sokoban level package file
2010 if (strSuffixLower(basename, ".xsb") &&
2011 strchr(basename, '%') == NULL)
2012 return LEVEL_FILE_TYPE_SB;
2014 // ---------- try to determine file type from filesize ----------
2016 checked_free(filename);
2017 filename = getPath2(getCurrentLevelDir(), basename);
2019 if (stat(filename, &file_status) == 0)
2021 // check for typical filesize of a Supaplex level package file
2022 if (file_status.st_size == 170496)
2023 return LEVEL_FILE_TYPE_SP;
2026 return LEVEL_FILE_TYPE_UNKNOWN;
2029 static int getFileTypeFromMagicBytes(char *filename, int type)
2033 if ((file = openFile(filename, MODE_READ)))
2035 char chunk_name[CHUNK_ID_LEN + 1];
2037 getFileChunkBE(file, chunk_name, NULL);
2039 if (strEqual(chunk_name, "MMII") ||
2040 strEqual(chunk_name, "MIRR"))
2041 type = LEVEL_FILE_TYPE_MM;
2049 static boolean checkForPackageFromBasename(char *basename)
2051 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2052 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2054 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2057 static char *getSingleLevelBasenameExt(int nr, char *extension)
2059 static char basename[MAX_FILENAME_LEN];
2062 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2064 sprintf(basename, "%03d.%s", nr, extension);
2069 static char *getSingleLevelBasename(int nr)
2071 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2074 static char *getPackedLevelBasename(int type)
2076 static char basename[MAX_FILENAME_LEN];
2077 char *directory = getCurrentLevelDir();
2079 DirectoryEntry *dir_entry;
2081 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2083 if ((dir = openDirectory(directory)) == NULL)
2085 Warn("cannot read current level directory '%s'", directory);
2090 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2092 char *entry_basename = dir_entry->basename;
2093 int entry_type = getFileTypeFromBasename(entry_basename);
2095 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2097 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2100 strcpy(basename, entry_basename);
2107 closeDirectory(dir);
2112 static char *getSingleLevelFilename(int nr)
2114 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2117 #if ENABLE_UNUSED_CODE
2118 static char *getPackedLevelFilename(int type)
2120 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2124 char *getDefaultLevelFilename(int nr)
2126 return getSingleLevelFilename(nr);
2129 #if ENABLE_UNUSED_CODE
2130 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2134 lfi->packed = FALSE;
2136 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2137 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2141 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2142 int type, char *format, ...)
2144 static char basename[MAX_FILENAME_LEN];
2147 va_start(ap, format);
2148 vsprintf(basename, format, ap);
2152 lfi->packed = FALSE;
2154 setString(&lfi->basename, basename);
2155 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2158 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2164 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2165 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2168 static int getFiletypeFromID(char *filetype_id)
2170 char *filetype_id_lower;
2171 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2174 if (filetype_id == NULL)
2175 return LEVEL_FILE_TYPE_UNKNOWN;
2177 filetype_id_lower = getStringToLower(filetype_id);
2179 for (i = 0; filetype_id_list[i].id != NULL; i++)
2181 char *id_lower = getStringToLower(filetype_id_list[i].id);
2183 if (strEqual(filetype_id_lower, id_lower))
2184 filetype = filetype_id_list[i].filetype;
2188 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2192 free(filetype_id_lower);
2197 char *getLocalLevelTemplateFilename(void)
2199 return getDefaultLevelFilename(-1);
2202 char *getGlobalLevelTemplateFilename(void)
2204 // global variable "leveldir_current" must be modified in the loop below
2205 LevelDirTree *leveldir_current_last = leveldir_current;
2206 char *filename = NULL;
2208 // check for template level in path from current to topmost tree node
2210 while (leveldir_current != NULL)
2212 filename = getDefaultLevelFilename(-1);
2214 if (fileExists(filename))
2217 leveldir_current = leveldir_current->node_parent;
2220 // restore global variable "leveldir_current" modified in above loop
2221 leveldir_current = leveldir_current_last;
2226 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2230 // special case: level number is negative => check for level template file
2233 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2234 getSingleLevelBasename(-1));
2236 // replace local level template filename with global template filename
2237 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2239 // no fallback if template file not existing
2243 // special case: check for file name/pattern specified in "levelinfo.conf"
2244 if (leveldir_current->level_filename != NULL)
2246 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2248 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2249 leveldir_current->level_filename, nr);
2251 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2253 if (fileExists(lfi->filename))
2256 else if (leveldir_current->level_filetype != NULL)
2258 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2260 // check for specified native level file with standard file name
2261 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2262 "%03d.%s", nr, LEVELFILE_EXTENSION);
2263 if (fileExists(lfi->filename))
2267 // check for native Rocks'n'Diamonds level file
2268 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2269 "%03d.%s", nr, LEVELFILE_EXTENSION);
2270 if (fileExists(lfi->filename))
2273 // check for Emerald Mine level file (V1)
2274 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2275 'a' + (nr / 10) % 26, '0' + nr % 10);
2276 if (fileExists(lfi->filename))
2278 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2279 'A' + (nr / 10) % 26, '0' + nr % 10);
2280 if (fileExists(lfi->filename))
2283 // check for Emerald Mine level file (V2 to V5)
2284 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2285 if (fileExists(lfi->filename))
2288 // check for Emerald Mine level file (V6 / single mode)
2289 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2290 if (fileExists(lfi->filename))
2292 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2293 if (fileExists(lfi->filename))
2296 // check for Emerald Mine level file (V6 / teamwork mode)
2297 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2298 if (fileExists(lfi->filename))
2300 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2301 if (fileExists(lfi->filename))
2304 // check for various packed level file formats
2305 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2306 if (fileExists(lfi->filename))
2309 // no known level file found -- use default values (and fail later)
2310 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2311 "%03d.%s", nr, LEVELFILE_EXTENSION);
2314 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2316 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2317 lfi->type = getFileTypeFromBasename(lfi->basename);
2319 if (lfi->type == LEVEL_FILE_TYPE_RND)
2320 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2323 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2325 // always start with reliable default values
2326 setFileInfoToDefaults(level_file_info);
2328 level_file_info->nr = nr; // set requested level number
2330 determineLevelFileInfo_Filename(level_file_info);
2331 determineLevelFileInfo_Filetype(level_file_info);
2334 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2335 struct LevelFileInfo *lfi_to)
2337 lfi_to->nr = lfi_from->nr;
2338 lfi_to->type = lfi_from->type;
2339 lfi_to->packed = lfi_from->packed;
2341 setString(&lfi_to->basename, lfi_from->basename);
2342 setString(&lfi_to->filename, lfi_from->filename);
2345 // ----------------------------------------------------------------------------
2346 // functions for loading R'n'D level
2347 // ----------------------------------------------------------------------------
2349 int getMappedElement(int element)
2351 // remap some (historic, now obsolete) elements
2355 case EL_PLAYER_OBSOLETE:
2356 element = EL_PLAYER_1;
2359 case EL_KEY_OBSOLETE:
2363 case EL_EM_KEY_1_FILE_OBSOLETE:
2364 element = EL_EM_KEY_1;
2367 case EL_EM_KEY_2_FILE_OBSOLETE:
2368 element = EL_EM_KEY_2;
2371 case EL_EM_KEY_3_FILE_OBSOLETE:
2372 element = EL_EM_KEY_3;
2375 case EL_EM_KEY_4_FILE_OBSOLETE:
2376 element = EL_EM_KEY_4;
2379 case EL_ENVELOPE_OBSOLETE:
2380 element = EL_ENVELOPE_1;
2388 if (element >= NUM_FILE_ELEMENTS)
2390 Warn("invalid level element %d", element);
2392 element = EL_UNKNOWN;
2400 static int getMappedElementByVersion(int element, int game_version)
2402 // remap some elements due to certain game version
2404 if (game_version <= VERSION_IDENT(2,2,0,0))
2406 // map game font elements
2407 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2408 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2409 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2410 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2413 if (game_version < VERSION_IDENT(3,0,0,0))
2415 // map Supaplex gravity tube elements
2416 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2417 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2418 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2419 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2426 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2428 level->file_version = getFileVersion(file);
2429 level->game_version = getFileVersion(file);
2434 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2436 level->creation_date.year = getFile16BitBE(file);
2437 level->creation_date.month = getFile8Bit(file);
2438 level->creation_date.day = getFile8Bit(file);
2440 level->creation_date.src = DATE_SRC_LEVELFILE;
2445 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2447 int initial_player_stepsize;
2448 int initial_player_gravity;
2451 level->fieldx = getFile8Bit(file);
2452 level->fieldy = getFile8Bit(file);
2454 level->time = getFile16BitBE(file);
2455 level->gems_needed = getFile16BitBE(file);
2457 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2458 level->name[i] = getFile8Bit(file);
2459 level->name[MAX_LEVEL_NAME_LEN] = 0;
2461 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2462 level->score[i] = getFile8Bit(file);
2464 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2465 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2466 for (y = 0; y < 3; y++)
2467 for (x = 0; x < 3; x++)
2468 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2470 level->amoeba_speed = getFile8Bit(file);
2471 level->time_magic_wall = getFile8Bit(file);
2472 level->time_wheel = getFile8Bit(file);
2473 level->amoeba_content = getMappedElement(getFile8Bit(file));
2475 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2478 for (i = 0; i < MAX_PLAYERS; i++)
2479 level->initial_player_stepsize[i] = initial_player_stepsize;
2481 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2483 for (i = 0; i < MAX_PLAYERS; i++)
2484 level->initial_player_gravity[i] = initial_player_gravity;
2486 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2487 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2489 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2491 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2492 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2493 level->can_move_into_acid_bits = getFile32BitBE(file);
2494 level->dont_collide_with_bits = getFile8Bit(file);
2496 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2497 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2499 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2500 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2501 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2503 level->game_engine_type = getFile8Bit(file);
2505 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2510 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2514 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2515 level->name[i] = getFile8Bit(file);
2516 level->name[MAX_LEVEL_NAME_LEN] = 0;
2521 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2525 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2526 level->author[i] = getFile8Bit(file);
2527 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2532 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2535 int chunk_size_expected = level->fieldx * level->fieldy;
2537 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2538 stored with 16-bit encoding (and should be twice as big then).
2539 Even worse, playfield data was stored 16-bit when only yamyam content
2540 contained 16-bit elements and vice versa. */
2542 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2543 chunk_size_expected *= 2;
2545 if (chunk_size_expected != chunk_size)
2547 ReadUnusedBytesFromFile(file, chunk_size);
2548 return chunk_size_expected;
2551 for (y = 0; y < level->fieldy; y++)
2552 for (x = 0; x < level->fieldx; x++)
2553 level->field[x][y] =
2554 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2559 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2562 int header_size = 4;
2563 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2564 int chunk_size_expected = header_size + content_size;
2566 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2567 stored with 16-bit encoding (and should be twice as big then).
2568 Even worse, playfield data was stored 16-bit when only yamyam content
2569 contained 16-bit elements and vice versa. */
2571 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2572 chunk_size_expected += content_size;
2574 if (chunk_size_expected != chunk_size)
2576 ReadUnusedBytesFromFile(file, chunk_size);
2577 return chunk_size_expected;
2581 level->num_yamyam_contents = getFile8Bit(file);
2585 // correct invalid number of content fields -- should never happen
2586 if (level->num_yamyam_contents < 1 ||
2587 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2588 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2590 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2591 for (y = 0; y < 3; y++)
2592 for (x = 0; x < 3; x++)
2593 level->yamyam_content[i].e[x][y] =
2594 getMappedElement(level->encoding_16bit_field ?
2595 getFile16BitBE(file) : getFile8Bit(file));
2599 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2604 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2606 element = getMappedElement(getFile16BitBE(file));
2607 num_contents = getFile8Bit(file);
2609 getFile8Bit(file); // content x size (unused)
2610 getFile8Bit(file); // content y size (unused)
2612 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2614 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2615 for (y = 0; y < 3; y++)
2616 for (x = 0; x < 3; x++)
2617 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2619 // correct invalid number of content fields -- should never happen
2620 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2621 num_contents = STD_ELEMENT_CONTENTS;
2623 if (element == EL_YAMYAM)
2625 level->num_yamyam_contents = num_contents;
2627 for (i = 0; i < num_contents; i++)
2628 for (y = 0; y < 3; y++)
2629 for (x = 0; x < 3; x++)
2630 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2632 else if (element == EL_BD_AMOEBA)
2634 level->amoeba_content = content_array[0][0][0];
2638 Warn("cannot load content for element '%d'", element);
2644 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2650 int chunk_size_expected;
2652 element = getMappedElement(getFile16BitBE(file));
2653 if (!IS_ENVELOPE(element))
2654 element = EL_ENVELOPE_1;
2656 envelope_nr = element - EL_ENVELOPE_1;
2658 envelope_len = getFile16BitBE(file);
2660 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2661 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2663 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2665 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2666 if (chunk_size_expected != chunk_size)
2668 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2669 return chunk_size_expected;
2672 for (i = 0; i < envelope_len; i++)
2673 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2678 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2680 int num_changed_custom_elements = getFile16BitBE(file);
2681 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2684 if (chunk_size_expected != chunk_size)
2686 ReadUnusedBytesFromFile(file, chunk_size - 2);
2687 return chunk_size_expected;
2690 for (i = 0; i < num_changed_custom_elements; i++)
2692 int element = getMappedElement(getFile16BitBE(file));
2693 int properties = getFile32BitBE(file);
2695 if (IS_CUSTOM_ELEMENT(element))
2696 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2698 Warn("invalid custom element number %d", element);
2700 // older game versions that wrote level files with CUS1 chunks used
2701 // different default push delay values (not yet stored in level file)
2702 element_info[element].push_delay_fixed = 2;
2703 element_info[element].push_delay_random = 8;
2706 level->file_has_custom_elements = TRUE;
2711 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2713 int num_changed_custom_elements = getFile16BitBE(file);
2714 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2717 if (chunk_size_expected != chunk_size)
2719 ReadUnusedBytesFromFile(file, chunk_size - 2);
2720 return chunk_size_expected;
2723 for (i = 0; i < num_changed_custom_elements; i++)
2725 int element = getMappedElement(getFile16BitBE(file));
2726 int custom_target_element = getMappedElement(getFile16BitBE(file));
2728 if (IS_CUSTOM_ELEMENT(element))
2729 element_info[element].change->target_element = custom_target_element;
2731 Warn("invalid custom element number %d", element);
2734 level->file_has_custom_elements = TRUE;
2739 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2741 int num_changed_custom_elements = getFile16BitBE(file);
2742 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2745 if (chunk_size_expected != chunk_size)
2747 ReadUnusedBytesFromFile(file, chunk_size - 2);
2748 return chunk_size_expected;
2751 for (i = 0; i < num_changed_custom_elements; i++)
2753 int element = getMappedElement(getFile16BitBE(file));
2754 struct ElementInfo *ei = &element_info[element];
2755 unsigned int event_bits;
2757 if (!IS_CUSTOM_ELEMENT(element))
2759 Warn("invalid custom element number %d", element);
2761 element = EL_INTERNAL_DUMMY;
2764 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2765 ei->description[j] = getFile8Bit(file);
2766 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2768 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2770 // some free bytes for future properties and padding
2771 ReadUnusedBytesFromFile(file, 7);
2773 ei->use_gfx_element = getFile8Bit(file);
2774 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2776 ei->collect_score_initial = getFile8Bit(file);
2777 ei->collect_count_initial = getFile8Bit(file);
2779 ei->push_delay_fixed = getFile16BitBE(file);
2780 ei->push_delay_random = getFile16BitBE(file);
2781 ei->move_delay_fixed = getFile16BitBE(file);
2782 ei->move_delay_random = getFile16BitBE(file);
2784 ei->move_pattern = getFile16BitBE(file);
2785 ei->move_direction_initial = getFile8Bit(file);
2786 ei->move_stepsize = getFile8Bit(file);
2788 for (y = 0; y < 3; y++)
2789 for (x = 0; x < 3; x++)
2790 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2792 event_bits = getFile32BitBE(file);
2793 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2794 if (event_bits & (1 << j))
2795 ei->change->has_event[j] = TRUE;
2797 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2799 ei->change->delay_fixed = getFile16BitBE(file);
2800 ei->change->delay_random = getFile16BitBE(file);
2801 ei->change->delay_frames = getFile16BitBE(file);
2803 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2805 ei->change->explode = getFile8Bit(file);
2806 ei->change->use_target_content = getFile8Bit(file);
2807 ei->change->only_if_complete = getFile8Bit(file);
2808 ei->change->use_random_replace = getFile8Bit(file);
2810 ei->change->random_percentage = getFile8Bit(file);
2811 ei->change->replace_when = getFile8Bit(file);
2813 for (y = 0; y < 3; y++)
2814 for (x = 0; x < 3; x++)
2815 ei->change->target_content.e[x][y] =
2816 getMappedElement(getFile16BitBE(file));
2818 ei->slippery_type = getFile8Bit(file);
2820 // some free bytes for future properties and padding
2821 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2823 // mark that this custom element has been modified
2824 ei->modified_settings = TRUE;
2827 level->file_has_custom_elements = TRUE;
2832 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2834 struct ElementInfo *ei;
2835 int chunk_size_expected;
2839 // ---------- custom element base property values (96 bytes) ----------------
2841 element = getMappedElement(getFile16BitBE(file));
2843 if (!IS_CUSTOM_ELEMENT(element))
2845 Warn("invalid custom element number %d", element);
2847 ReadUnusedBytesFromFile(file, chunk_size - 2);
2852 ei = &element_info[element];
2854 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2855 ei->description[i] = getFile8Bit(file);
2856 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2858 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2860 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2862 ei->num_change_pages = getFile8Bit(file);
2864 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2865 if (chunk_size_expected != chunk_size)
2867 ReadUnusedBytesFromFile(file, chunk_size - 43);
2868 return chunk_size_expected;
2871 ei->ce_value_fixed_initial = getFile16BitBE(file);
2872 ei->ce_value_random_initial = getFile16BitBE(file);
2873 ei->use_last_ce_value = getFile8Bit(file);
2875 ei->use_gfx_element = getFile8Bit(file);
2876 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2878 ei->collect_score_initial = getFile8Bit(file);
2879 ei->collect_count_initial = getFile8Bit(file);
2881 ei->drop_delay_fixed = getFile8Bit(file);
2882 ei->push_delay_fixed = getFile8Bit(file);
2883 ei->drop_delay_random = getFile8Bit(file);
2884 ei->push_delay_random = getFile8Bit(file);
2885 ei->move_delay_fixed = getFile16BitBE(file);
2886 ei->move_delay_random = getFile16BitBE(file);
2888 // bits 0 - 15 of "move_pattern" ...
2889 ei->move_pattern = getFile16BitBE(file);
2890 ei->move_direction_initial = getFile8Bit(file);
2891 ei->move_stepsize = getFile8Bit(file);
2893 ei->slippery_type = getFile8Bit(file);
2895 for (y = 0; y < 3; y++)
2896 for (x = 0; x < 3; x++)
2897 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2899 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2900 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2901 ei->move_leave_type = getFile8Bit(file);
2903 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2904 ei->move_pattern |= (getFile16BitBE(file) << 16);
2906 ei->access_direction = getFile8Bit(file);
2908 ei->explosion_delay = getFile8Bit(file);
2909 ei->ignition_delay = getFile8Bit(file);
2910 ei->explosion_type = getFile8Bit(file);
2912 // some free bytes for future custom property values and padding
2913 ReadUnusedBytesFromFile(file, 1);
2915 // ---------- change page property values (48 bytes) ------------------------
2917 setElementChangePages(ei, ei->num_change_pages);
2919 for (i = 0; i < ei->num_change_pages; i++)
2921 struct ElementChangeInfo *change = &ei->change_page[i];
2922 unsigned int event_bits;
2924 // always start with reliable default values
2925 setElementChangeInfoToDefaults(change);
2927 // bits 0 - 31 of "has_event[]" ...
2928 event_bits = getFile32BitBE(file);
2929 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2930 if (event_bits & (1 << j))
2931 change->has_event[j] = TRUE;
2933 change->target_element = getMappedElement(getFile16BitBE(file));
2935 change->delay_fixed = getFile16BitBE(file);
2936 change->delay_random = getFile16BitBE(file);
2937 change->delay_frames = getFile16BitBE(file);
2939 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2941 change->explode = getFile8Bit(file);
2942 change->use_target_content = getFile8Bit(file);
2943 change->only_if_complete = getFile8Bit(file);
2944 change->use_random_replace = getFile8Bit(file);
2946 change->random_percentage = getFile8Bit(file);
2947 change->replace_when = getFile8Bit(file);
2949 for (y = 0; y < 3; y++)
2950 for (x = 0; x < 3; x++)
2951 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2953 change->can_change = getFile8Bit(file);
2955 change->trigger_side = getFile8Bit(file);
2957 change->trigger_player = getFile8Bit(file);
2958 change->trigger_page = getFile8Bit(file);
2960 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2961 CH_PAGE_ANY : (1 << change->trigger_page));
2963 change->has_action = getFile8Bit(file);
2964 change->action_type = getFile8Bit(file);
2965 change->action_mode = getFile8Bit(file);
2966 change->action_arg = getFile16BitBE(file);
2968 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2969 event_bits = getFile8Bit(file);
2970 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2971 if (event_bits & (1 << (j - 32)))
2972 change->has_event[j] = TRUE;
2975 // mark this custom element as modified
2976 ei->modified_settings = TRUE;
2978 level->file_has_custom_elements = TRUE;
2983 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2985 struct ElementInfo *ei;
2986 struct ElementGroupInfo *group;
2990 element = getMappedElement(getFile16BitBE(file));
2992 if (!IS_GROUP_ELEMENT(element))
2994 Warn("invalid group element number %d", element);
2996 ReadUnusedBytesFromFile(file, chunk_size - 2);
3001 ei = &element_info[element];
3003 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3004 ei->description[i] = getFile8Bit(file);
3005 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3007 group = element_info[element].group;
3009 group->num_elements = getFile8Bit(file);
3011 ei->use_gfx_element = getFile8Bit(file);
3012 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3014 group->choice_mode = getFile8Bit(file);
3016 // some free bytes for future values and padding
3017 ReadUnusedBytesFromFile(file, 3);
3019 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3020 group->element[i] = getMappedElement(getFile16BitBE(file));
3022 // mark this group element as modified
3023 element_info[element].modified_settings = TRUE;
3025 level->file_has_custom_elements = TRUE;
3030 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3031 int element, int real_element)
3033 int micro_chunk_size = 0;
3034 int conf_type = getFile8Bit(file);
3035 int byte_mask = conf_type & CONF_MASK_BYTES;
3036 boolean element_found = FALSE;
3039 micro_chunk_size += 1;
3041 if (byte_mask == CONF_MASK_MULTI_BYTES)
3043 int num_bytes = getFile16BitBE(file);
3044 byte *buffer = checked_malloc(num_bytes);
3046 ReadBytesFromFile(file, buffer, num_bytes);
3048 for (i = 0; conf[i].data_type != -1; i++)
3050 if (conf[i].element == element &&
3051 conf[i].conf_type == conf_type)
3053 int data_type = conf[i].data_type;
3054 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3055 int max_num_entities = conf[i].max_num_entities;
3057 if (num_entities > max_num_entities)
3059 Warn("truncating number of entities for element %d from %d to %d",
3060 element, num_entities, max_num_entities);
3062 num_entities = max_num_entities;
3065 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3066 data_type == TYPE_CONTENT_LIST))
3068 // for element and content lists, zero entities are not allowed
3069 Warn("found empty list of entities for element %d", element);
3071 // do not set "num_entities" here to prevent reading behind buffer
3073 *(int *)(conf[i].num_entities) = 1; // at least one is required
3077 *(int *)(conf[i].num_entities) = num_entities;
3080 element_found = TRUE;
3082 if (data_type == TYPE_STRING)
3084 char *string = (char *)(conf[i].value);
3087 for (j = 0; j < max_num_entities; j++)
3088 string[j] = (j < num_entities ? buffer[j] : '\0');
3090 else if (data_type == TYPE_ELEMENT_LIST)
3092 int *element_array = (int *)(conf[i].value);
3095 for (j = 0; j < num_entities; j++)
3097 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3099 else if (data_type == TYPE_CONTENT_LIST)
3101 struct Content *content= (struct Content *)(conf[i].value);
3104 for (c = 0; c < num_entities; c++)
3105 for (y = 0; y < 3; y++)
3106 for (x = 0; x < 3; x++)
3107 content[c].e[x][y] =
3108 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3111 element_found = FALSE;
3117 checked_free(buffer);
3119 micro_chunk_size += 2 + num_bytes;
3121 else // constant size configuration data (1, 2 or 4 bytes)
3123 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3124 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3125 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3127 for (i = 0; conf[i].data_type != -1; i++)
3129 if (conf[i].element == element &&
3130 conf[i].conf_type == conf_type)
3132 int data_type = conf[i].data_type;
3134 if (data_type == TYPE_ELEMENT)
3135 value = getMappedElement(value);
3137 if (data_type == TYPE_BOOLEAN)
3138 *(boolean *)(conf[i].value) = value;
3140 *(int *) (conf[i].value) = value;
3142 element_found = TRUE;
3148 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3153 char *error_conf_chunk_bytes =
3154 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3155 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3156 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3157 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3158 int error_element = real_element;
3160 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3161 error_conf_chunk_bytes, error_conf_chunk_token,
3162 error_element, EL_NAME(error_element));
3165 return micro_chunk_size;
3168 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3170 int real_chunk_size = 0;
3172 li = *level; // copy level data into temporary buffer
3174 while (!checkEndOfFile(file))
3176 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3178 if (real_chunk_size >= chunk_size)
3182 *level = li; // copy temporary buffer back to level data
3184 return real_chunk_size;
3187 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3189 int real_chunk_size = 0;
3191 li = *level; // copy level data into temporary buffer
3193 while (!checkEndOfFile(file))
3195 int element = getMappedElement(getFile16BitBE(file));
3197 real_chunk_size += 2;
3198 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3200 if (real_chunk_size >= chunk_size)
3204 *level = li; // copy temporary buffer back to level data
3206 return real_chunk_size;
3209 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3211 int real_chunk_size = 0;
3213 li = *level; // copy level data into temporary buffer
3215 while (!checkEndOfFile(file))
3217 int element = getMappedElement(getFile16BitBE(file));
3219 real_chunk_size += 2;
3220 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3222 if (real_chunk_size >= chunk_size)
3226 *level = li; // copy temporary buffer back to level data
3228 return real_chunk_size;
3231 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3233 int element = getMappedElement(getFile16BitBE(file));
3234 int envelope_nr = element - EL_ENVELOPE_1;
3235 int real_chunk_size = 2;
3237 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3239 while (!checkEndOfFile(file))
3241 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3244 if (real_chunk_size >= chunk_size)
3248 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3250 return real_chunk_size;
3253 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3255 int element = getMappedElement(getFile16BitBE(file));
3256 int real_chunk_size = 2;
3257 struct ElementInfo *ei = &element_info[element];
3260 xx_ei = *ei; // copy element data into temporary buffer
3262 xx_ei.num_change_pages = -1;
3264 while (!checkEndOfFile(file))
3266 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3268 if (xx_ei.num_change_pages != -1)
3271 if (real_chunk_size >= chunk_size)
3277 if (ei->num_change_pages == -1)
3279 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3282 ei->num_change_pages = 1;
3284 setElementChangePages(ei, 1);
3285 setElementChangeInfoToDefaults(ei->change);
3287 return real_chunk_size;
3290 // initialize number of change pages stored for this custom element
3291 setElementChangePages(ei, ei->num_change_pages);
3292 for (i = 0; i < ei->num_change_pages; i++)
3293 setElementChangeInfoToDefaults(&ei->change_page[i]);
3295 // start with reading properties for the first change page
3296 xx_current_change_page = 0;
3298 while (!checkEndOfFile(file))
3300 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3302 xx_change = *change; // copy change data into temporary buffer
3304 resetEventBits(); // reset bits; change page might have changed
3306 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3309 *change = xx_change;
3311 setEventFlagsFromEventBits(change);
3313 if (real_chunk_size >= chunk_size)
3317 level->file_has_custom_elements = TRUE;
3319 return real_chunk_size;
3322 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3324 int element = getMappedElement(getFile16BitBE(file));
3325 int real_chunk_size = 2;
3326 struct ElementInfo *ei = &element_info[element];
3327 struct ElementGroupInfo *group = ei->group;
3329 xx_ei = *ei; // copy element data into temporary buffer
3330 xx_group = *group; // copy group data into temporary buffer
3332 while (!checkEndOfFile(file))
3334 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3337 if (real_chunk_size >= chunk_size)
3344 level->file_has_custom_elements = TRUE;
3346 return real_chunk_size;
3349 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3350 struct LevelFileInfo *level_file_info,
3351 boolean level_info_only)
3353 char *filename = level_file_info->filename;
3354 char cookie[MAX_LINE_LEN];
3355 char chunk_name[CHUNK_ID_LEN + 1];
3359 if (!(file = openFile(filename, MODE_READ)))
3361 level->no_valid_file = TRUE;
3362 level->no_level_file = TRUE;
3364 if (level_info_only)
3367 Warn("cannot read level '%s' -- using empty level", filename);
3369 if (!setup.editor.use_template_for_new_levels)
3372 // if level file not found, try to initialize level data from template
3373 filename = getGlobalLevelTemplateFilename();
3375 if (!(file = openFile(filename, MODE_READ)))
3378 // default: for empty levels, use level template for custom elements
3379 level->use_custom_template = TRUE;
3381 level->no_valid_file = FALSE;
3384 getFileChunkBE(file, chunk_name, NULL);
3385 if (strEqual(chunk_name, "RND1"))
3387 getFile32BitBE(file); // not used
3389 getFileChunkBE(file, chunk_name, NULL);
3390 if (!strEqual(chunk_name, "CAVE"))
3392 level->no_valid_file = TRUE;
3394 Warn("unknown format of level file '%s'", filename);
3401 else // check for pre-2.0 file format with cookie string
3403 strcpy(cookie, chunk_name);
3404 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3406 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3407 cookie[strlen(cookie) - 1] = '\0';
3409 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3411 level->no_valid_file = TRUE;
3413 Warn("unknown format of level file '%s'", filename);
3420 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3422 level->no_valid_file = TRUE;
3424 Warn("unsupported version of level file '%s'", filename);
3431 // pre-2.0 level files have no game version, so use file version here
3432 level->game_version = level->file_version;
3435 if (level->file_version < FILE_VERSION_1_2)
3437 // level files from versions before 1.2.0 without chunk structure
3438 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3439 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3447 int (*loader)(File *, int, struct LevelInfo *);
3451 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3452 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3453 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3454 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3455 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3456 { "INFO", -1, LoadLevel_INFO },
3457 { "BODY", -1, LoadLevel_BODY },
3458 { "CONT", -1, LoadLevel_CONT },
3459 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3460 { "CNT3", -1, LoadLevel_CNT3 },
3461 { "CUS1", -1, LoadLevel_CUS1 },
3462 { "CUS2", -1, LoadLevel_CUS2 },
3463 { "CUS3", -1, LoadLevel_CUS3 },
3464 { "CUS4", -1, LoadLevel_CUS4 },
3465 { "GRP1", -1, LoadLevel_GRP1 },
3466 { "CONF", -1, LoadLevel_CONF },
3467 { "ELEM", -1, LoadLevel_ELEM },
3468 { "NOTE", -1, LoadLevel_NOTE },
3469 { "CUSX", -1, LoadLevel_CUSX },
3470 { "GRPX", -1, LoadLevel_GRPX },
3475 while (getFileChunkBE(file, chunk_name, &chunk_size))
3479 while (chunk_info[i].name != NULL &&
3480 !strEqual(chunk_name, chunk_info[i].name))
3483 if (chunk_info[i].name == NULL)
3485 Warn("unknown chunk '%s' in level file '%s'",
3486 chunk_name, filename);
3488 ReadUnusedBytesFromFile(file, chunk_size);
3490 else if (chunk_info[i].size != -1 &&
3491 chunk_info[i].size != chunk_size)
3493 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3494 chunk_size, chunk_name, filename);
3496 ReadUnusedBytesFromFile(file, chunk_size);
3500 // call function to load this level chunk
3501 int chunk_size_expected =
3502 (chunk_info[i].loader)(file, chunk_size, level);
3504 // the size of some chunks cannot be checked before reading other
3505 // chunks first (like "HEAD" and "BODY") that contain some header
3506 // information, so check them here
3507 if (chunk_size_expected != chunk_size)
3509 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3510 chunk_size, chunk_name, filename);
3520 // ----------------------------------------------------------------------------
3521 // functions for loading EM level
3522 // ----------------------------------------------------------------------------
3524 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3526 static int ball_xy[8][2] =
3537 struct LevelInfo_EM *level_em = level->native_em_level;
3538 struct CAVE *cav = level_em->cav;
3541 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3542 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3544 cav->time_seconds = level->time;
3545 cav->gems_needed = level->gems_needed;
3547 cav->emerald_score = level->score[SC_EMERALD];
3548 cav->diamond_score = level->score[SC_DIAMOND];
3549 cav->alien_score = level->score[SC_ROBOT];
3550 cav->tank_score = level->score[SC_SPACESHIP];
3551 cav->bug_score = level->score[SC_BUG];
3552 cav->eater_score = level->score[SC_YAMYAM];
3553 cav->nut_score = level->score[SC_NUT];
3554 cav->dynamite_score = level->score[SC_DYNAMITE];
3555 cav->key_score = level->score[SC_KEY];
3556 cav->exit_score = level->score[SC_TIME_BONUS];
3558 cav->num_eater_arrays = level->num_yamyam_contents;
3560 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3561 for (y = 0; y < 3; y++)
3562 for (x = 0; x < 3; x++)
3563 cav->eater_array[i][y * 3 + x] =
3564 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3566 cav->amoeba_time = level->amoeba_speed;
3567 cav->wonderwall_time = level->time_magic_wall;
3568 cav->wheel_time = level->time_wheel;
3570 cav->android_move_time = level->android_move_time;
3571 cav->android_clone_time = level->android_clone_time;
3572 cav->ball_random = level->ball_random;
3573 cav->ball_active = level->ball_active_initial;
3574 cav->ball_time = level->ball_time;
3575 cav->num_ball_arrays = level->num_ball_contents;
3577 cav->lenses_score = level->lenses_score;
3578 cav->magnify_score = level->magnify_score;
3579 cav->slurp_score = level->slurp_score;
3581 cav->lenses_time = level->lenses_time;
3582 cav->magnify_time = level->magnify_time;
3584 cav->wind_direction =
3585 map_direction_RND_to_EM(level->wind_direction_initial);
3587 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3588 for (j = 0; j < 8; j++)
3589 cav->ball_array[i][j] =
3590 map_element_RND_to_EM_cave(level->ball_content[i].
3591 e[ball_xy[j][0]][ball_xy[j][1]]);
3593 map_android_clone_elements_RND_to_EM(level);
3595 // first fill the complete playfield with the empty space element
3596 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3597 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3598 cav->cave[x][y] = Cblank;
3600 // then copy the real level contents from level file into the playfield
3601 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3603 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3605 if (level->field[x][y] == EL_AMOEBA_DEAD)
3606 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3608 cav->cave[x][y] = new_element;
3611 for (i = 0; i < MAX_PLAYERS; i++)
3613 cav->player_x[i] = -1;
3614 cav->player_y[i] = -1;
3617 // initialize player positions and delete players from the playfield
3618 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3620 if (ELEM_IS_PLAYER(level->field[x][y]))
3622 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3624 cav->player_x[player_nr] = x;
3625 cav->player_y[player_nr] = y;
3627 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3632 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3634 static int ball_xy[8][2] =
3645 struct LevelInfo_EM *level_em = level->native_em_level;
3646 struct CAVE *cav = level_em->cav;
3649 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3650 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3652 level->time = cav->time_seconds;
3653 level->gems_needed = cav->gems_needed;
3655 sprintf(level->name, "Level %d", level->file_info.nr);
3657 level->score[SC_EMERALD] = cav->emerald_score;
3658 level->score[SC_DIAMOND] = cav->diamond_score;
3659 level->score[SC_ROBOT] = cav->alien_score;
3660 level->score[SC_SPACESHIP] = cav->tank_score;
3661 level->score[SC_BUG] = cav->bug_score;
3662 level->score[SC_YAMYAM] = cav->eater_score;
3663 level->score[SC_NUT] = cav->nut_score;
3664 level->score[SC_DYNAMITE] = cav->dynamite_score;
3665 level->score[SC_KEY] = cav->key_score;
3666 level->score[SC_TIME_BONUS] = cav->exit_score;
3668 level->num_yamyam_contents = cav->num_eater_arrays;
3670 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3671 for (y = 0; y < 3; y++)
3672 for (x = 0; x < 3; x++)
3673 level->yamyam_content[i].e[x][y] =
3674 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3676 level->amoeba_speed = cav->amoeba_time;
3677 level->time_magic_wall = cav->wonderwall_time;
3678 level->time_wheel = cav->wheel_time;
3680 level->android_move_time = cav->android_move_time;
3681 level->android_clone_time = cav->android_clone_time;
3682 level->ball_random = cav->ball_random;
3683 level->ball_active_initial = cav->ball_active;
3684 level->ball_time = cav->ball_time;
3685 level->num_ball_contents = cav->num_ball_arrays;
3687 level->lenses_score = cav->lenses_score;
3688 level->magnify_score = cav->magnify_score;
3689 level->slurp_score = cav->slurp_score;
3691 level->lenses_time = cav->lenses_time;
3692 level->magnify_time = cav->magnify_time;
3694 level->wind_direction_initial =
3695 map_direction_EM_to_RND(cav->wind_direction);
3697 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3698 for (j = 0; j < 8; j++)
3699 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3700 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3702 map_android_clone_elements_EM_to_RND(level);
3704 // convert the playfield (some elements need special treatment)
3705 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3707 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3709 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3710 new_element = EL_AMOEBA_DEAD;
3712 level->field[x][y] = new_element;
3715 for (i = 0; i < MAX_PLAYERS; i++)
3717 // in case of all players set to the same field, use the first player
3718 int nr = MAX_PLAYERS - i - 1;
3719 int jx = cav->player_x[nr];
3720 int jy = cav->player_y[nr];
3722 if (jx != -1 && jy != -1)
3723 level->field[jx][jy] = EL_PLAYER_1 + nr;
3726 // time score is counted for each 10 seconds left in Emerald Mine levels
3727 level->time_score_base = 10;
3731 // ----------------------------------------------------------------------------
3732 // functions for loading SP level
3733 // ----------------------------------------------------------------------------
3735 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3737 struct LevelInfo_SP *level_sp = level->native_sp_level;
3738 LevelInfoType *header = &level_sp->header;
3741 level_sp->width = level->fieldx;
3742 level_sp->height = level->fieldy;
3744 for (x = 0; x < level->fieldx; x++)
3745 for (y = 0; y < level->fieldy; y++)
3746 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3748 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3750 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3751 header->LevelTitle[i] = level->name[i];
3752 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3754 header->InfotronsNeeded = level->gems_needed;
3756 header->SpecialPortCount = 0;
3758 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3760 boolean gravity_port_found = FALSE;
3761 boolean gravity_port_valid = FALSE;
3762 int gravity_port_flag;
3763 int gravity_port_base_element;
3764 int element = level->field[x][y];
3766 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3767 element <= EL_SP_GRAVITY_ON_PORT_UP)
3769 gravity_port_found = TRUE;
3770 gravity_port_valid = TRUE;
3771 gravity_port_flag = 1;
3772 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3774 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3775 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3777 gravity_port_found = TRUE;
3778 gravity_port_valid = TRUE;
3779 gravity_port_flag = 0;
3780 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3782 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3783 element <= EL_SP_GRAVITY_PORT_UP)
3785 // change R'n'D style gravity inverting special port to normal port
3786 // (there are no gravity inverting ports in native Supaplex engine)
3788 gravity_port_found = TRUE;
3789 gravity_port_valid = FALSE;
3790 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3793 if (gravity_port_found)
3795 if (gravity_port_valid &&
3796 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3798 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3800 port->PortLocation = (y * level->fieldx + x) * 2;
3801 port->Gravity = gravity_port_flag;
3803 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3805 header->SpecialPortCount++;
3809 // change special gravity port to normal port
3811 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3814 level_sp->playfield[x][y] = element - EL_SP_START;
3819 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3821 struct LevelInfo_SP *level_sp = level->native_sp_level;
3822 LevelInfoType *header = &level_sp->header;
3823 boolean num_invalid_elements = 0;
3826 level->fieldx = level_sp->width;
3827 level->fieldy = level_sp->height;
3829 for (x = 0; x < level->fieldx; x++)
3831 for (y = 0; y < level->fieldy; y++)
3833 int element_old = level_sp->playfield[x][y];
3834 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3836 if (element_new == EL_UNKNOWN)
3838 num_invalid_elements++;
3840 Debug("level:native:SP", "invalid element %d at position %d, %d",
3844 level->field[x][y] = element_new;
3848 if (num_invalid_elements > 0)
3849 Warn("found %d invalid elements%s", num_invalid_elements,
3850 (!options.debug ? " (use '--debug' for more details)" : ""));
3852 for (i = 0; i < MAX_PLAYERS; i++)
3853 level->initial_player_gravity[i] =
3854 (header->InitialGravity == 1 ? TRUE : FALSE);
3856 // skip leading spaces
3857 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3858 if (header->LevelTitle[i] != ' ')
3862 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3863 level->name[j] = header->LevelTitle[i];
3864 level->name[j] = '\0';
3866 // cut trailing spaces
3868 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3869 level->name[j - 1] = '\0';
3871 level->gems_needed = header->InfotronsNeeded;
3873 for (i = 0; i < header->SpecialPortCount; i++)
3875 SpecialPortType *port = &header->SpecialPort[i];
3876 int port_location = port->PortLocation;
3877 int gravity = port->Gravity;
3878 int port_x, port_y, port_element;
3880 port_x = (port_location / 2) % level->fieldx;
3881 port_y = (port_location / 2) / level->fieldx;
3883 if (port_x < 0 || port_x >= level->fieldx ||
3884 port_y < 0 || port_y >= level->fieldy)
3886 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3891 port_element = level->field[port_x][port_y];
3893 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3894 port_element > EL_SP_GRAVITY_PORT_UP)
3896 Warn("no special port at position (%d, %d)", port_x, port_y);
3901 // change previous (wrong) gravity inverting special port to either
3902 // gravity enabling special port or gravity disabling special port
3903 level->field[port_x][port_y] +=
3904 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3905 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3908 // change special gravity ports without database entries to normal ports
3909 for (x = 0; x < level->fieldx; x++)
3910 for (y = 0; y < level->fieldy; y++)
3911 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3912 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3913 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3915 level->time = 0; // no time limit
3916 level->amoeba_speed = 0;
3917 level->time_magic_wall = 0;
3918 level->time_wheel = 0;
3919 level->amoeba_content = EL_EMPTY;
3922 // original Supaplex does not use score values -- use default values
3924 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3925 level->score[i] = 0;
3928 // there are no yamyams in supaplex levels
3929 for (i = 0; i < level->num_yamyam_contents; i++)
3930 for (x = 0; x < 3; x++)
3931 for (y = 0; y < 3; y++)
3932 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3935 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3937 struct LevelInfo_SP *level_sp = level->native_sp_level;
3938 struct DemoInfo_SP *demo = &level_sp->demo;
3941 // always start with reliable default values
3942 demo->is_available = FALSE;
3945 if (TAPE_IS_EMPTY(tape))
3948 demo->level_nr = tape.level_nr; // (currently not used)
3950 level_sp->header.DemoRandomSeed = tape.random_seed;
3954 for (i = 0; i < tape.length; i++)
3956 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3957 int demo_repeat = tape.pos[i].delay;
3958 int demo_entries = (demo_repeat + 15) / 16;
3960 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3962 Warn("tape truncated: size exceeds maximum SP demo size %d",
3968 for (j = 0; j < demo_repeat / 16; j++)
3969 demo->data[demo->length++] = 0xf0 | demo_action;
3971 if (demo_repeat % 16)
3972 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3975 demo->is_available = TRUE;
3978 static void setTapeInfoToDefaults(void);
3980 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3982 struct LevelInfo_SP *level_sp = level->native_sp_level;
3983 struct DemoInfo_SP *demo = &level_sp->demo;
3984 char *filename = level->file_info.filename;
3987 // always start with reliable default values
3988 setTapeInfoToDefaults();
3990 if (!demo->is_available)
3993 tape.level_nr = demo->level_nr; // (currently not used)
3994 tape.random_seed = level_sp->header.DemoRandomSeed;
3996 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3999 tape.pos[tape.counter].delay = 0;
4001 for (i = 0; i < demo->length; i++)
4003 int demo_action = demo->data[i] & 0x0f;
4004 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4005 int tape_action = map_key_SP_to_RND(demo_action);
4006 int tape_repeat = demo_repeat + 1;
4007 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4008 boolean success = 0;
4011 for (j = 0; j < tape_repeat; j++)
4012 success = TapeAddAction(action);
4016 Warn("SP demo truncated: size exceeds maximum tape size %d",
4023 TapeHaltRecording();
4027 // ----------------------------------------------------------------------------
4028 // functions for loading MM level
4029 // ----------------------------------------------------------------------------
4031 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4033 struct LevelInfo_MM *level_mm = level->native_mm_level;
4036 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4037 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4039 level_mm->time = level->time;
4040 level_mm->kettles_needed = level->gems_needed;
4041 level_mm->auto_count_kettles = level->auto_count_gems;
4043 level_mm->laser_red = level->mm_laser_red;
4044 level_mm->laser_green = level->mm_laser_green;
4045 level_mm->laser_blue = level->mm_laser_blue;
4047 strcpy(level_mm->name, level->name);
4048 strcpy(level_mm->author, level->author);
4050 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4051 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4052 level_mm->score[SC_KEY] = level->score[SC_KEY];
4053 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4054 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4056 level_mm->amoeba_speed = level->amoeba_speed;
4057 level_mm->time_fuse = level->mm_time_fuse;
4058 level_mm->time_bomb = level->mm_time_bomb;
4059 level_mm->time_ball = level->mm_time_ball;
4060 level_mm->time_block = level->mm_time_block;
4062 for (x = 0; x < level->fieldx; x++)
4063 for (y = 0; y < level->fieldy; y++)
4065 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4068 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4070 struct LevelInfo_MM *level_mm = level->native_mm_level;
4073 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4074 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4076 level->time = level_mm->time;
4077 level->gems_needed = level_mm->kettles_needed;
4078 level->auto_count_gems = level_mm->auto_count_kettles;
4080 level->mm_laser_red = level_mm->laser_red;
4081 level->mm_laser_green = level_mm->laser_green;
4082 level->mm_laser_blue = level_mm->laser_blue;
4084 strcpy(level->name, level_mm->name);
4086 // only overwrite author from 'levelinfo.conf' if author defined in level
4087 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4088 strcpy(level->author, level_mm->author);
4090 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4091 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4092 level->score[SC_KEY] = level_mm->score[SC_KEY];
4093 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4094 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4096 level->amoeba_speed = level_mm->amoeba_speed;
4097 level->mm_time_fuse = level_mm->time_fuse;
4098 level->mm_time_bomb = level_mm->time_bomb;
4099 level->mm_time_ball = level_mm->time_ball;
4100 level->mm_time_block = level_mm->time_block;
4102 for (x = 0; x < level->fieldx; x++)
4103 for (y = 0; y < level->fieldy; y++)
4104 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4108 // ----------------------------------------------------------------------------
4109 // functions for loading DC level
4110 // ----------------------------------------------------------------------------
4112 #define DC_LEVEL_HEADER_SIZE 344
4114 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4117 static int last_data_encoded;
4121 int diff_hi, diff_lo;
4122 int data_hi, data_lo;
4123 unsigned short data_decoded;
4127 last_data_encoded = 0;
4134 diff = data_encoded - last_data_encoded;
4135 diff_hi = diff & ~0xff;
4136 diff_lo = diff & 0xff;
4140 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4141 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4142 data_hi = data_hi & 0xff00;
4144 data_decoded = data_hi | data_lo;
4146 last_data_encoded = data_encoded;
4148 offset1 = (offset1 + 1) % 31;
4149 offset2 = offset2 & 0xff;
4151 return data_decoded;
4154 static int getMappedElement_DC(int element)
4162 // 0x0117 - 0x036e: (?)
4165 // 0x042d - 0x0684: (?)
4181 element = EL_CRYSTAL;
4184 case 0x0e77: // quicksand (boulder)
4185 element = EL_QUICKSAND_FAST_FULL;
4188 case 0x0e99: // slow quicksand (boulder)
4189 element = EL_QUICKSAND_FULL;
4193 element = EL_EM_EXIT_OPEN;
4197 element = EL_EM_EXIT_CLOSED;
4201 element = EL_EM_STEEL_EXIT_OPEN;
4205 element = EL_EM_STEEL_EXIT_CLOSED;
4208 case 0x0f4f: // dynamite (lit 1)
4209 element = EL_EM_DYNAMITE_ACTIVE;
4212 case 0x0f57: // dynamite (lit 2)
4213 element = EL_EM_DYNAMITE_ACTIVE;
4216 case 0x0f5f: // dynamite (lit 3)
4217 element = EL_EM_DYNAMITE_ACTIVE;
4220 case 0x0f67: // dynamite (lit 4)
4221 element = EL_EM_DYNAMITE_ACTIVE;
4228 element = EL_AMOEBA_WET;
4232 element = EL_AMOEBA_DROP;
4236 element = EL_DC_MAGIC_WALL;
4240 element = EL_SPACESHIP_UP;
4244 element = EL_SPACESHIP_DOWN;
4248 element = EL_SPACESHIP_LEFT;
4252 element = EL_SPACESHIP_RIGHT;
4256 element = EL_BUG_UP;
4260 element = EL_BUG_DOWN;
4264 element = EL_BUG_LEFT;
4268 element = EL_BUG_RIGHT;
4272 element = EL_MOLE_UP;
4276 element = EL_MOLE_DOWN;
4280 element = EL_MOLE_LEFT;
4284 element = EL_MOLE_RIGHT;
4292 element = EL_YAMYAM_UP;
4296 element = EL_SWITCHGATE_OPEN;
4300 element = EL_SWITCHGATE_CLOSED;
4304 element = EL_DC_SWITCHGATE_SWITCH_UP;
4308 element = EL_TIMEGATE_CLOSED;
4311 case 0x144c: // conveyor belt switch (green)
4312 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4315 case 0x144f: // conveyor belt switch (red)
4316 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4319 case 0x1452: // conveyor belt switch (blue)
4320 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4324 element = EL_CONVEYOR_BELT_3_MIDDLE;
4328 element = EL_CONVEYOR_BELT_3_LEFT;
4332 element = EL_CONVEYOR_BELT_3_RIGHT;
4336 element = EL_CONVEYOR_BELT_1_MIDDLE;
4340 element = EL_CONVEYOR_BELT_1_LEFT;
4344 element = EL_CONVEYOR_BELT_1_RIGHT;
4348 element = EL_CONVEYOR_BELT_4_MIDDLE;
4352 element = EL_CONVEYOR_BELT_4_LEFT;
4356 element = EL_CONVEYOR_BELT_4_RIGHT;
4360 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4364 element = EL_EXPANDABLE_WALL_VERTICAL;
4368 element = EL_EXPANDABLE_WALL_ANY;
4371 case 0x14ce: // growing steel wall (left/right)
4372 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4375 case 0x14df: // growing steel wall (up/down)
4376 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4379 case 0x14e8: // growing steel wall (up/down/left/right)
4380 element = EL_EXPANDABLE_STEELWALL_ANY;
4384 element = EL_SHIELD_DEADLY;
4388 element = EL_EXTRA_TIME;
4396 element = EL_EMPTY_SPACE;
4399 case 0x1578: // quicksand (empty)
4400 element = EL_QUICKSAND_FAST_EMPTY;
4403 case 0x1579: // slow quicksand (empty)
4404 element = EL_QUICKSAND_EMPTY;
4414 element = EL_EM_DYNAMITE;
4417 case 0x15a1: // key (red)
4418 element = EL_EM_KEY_1;
4421 case 0x15a2: // key (yellow)
4422 element = EL_EM_KEY_2;
4425 case 0x15a3: // key (blue)
4426 element = EL_EM_KEY_4;
4429 case 0x15a4: // key (green)
4430 element = EL_EM_KEY_3;
4433 case 0x15a5: // key (white)
4434 element = EL_DC_KEY_WHITE;
4438 element = EL_WALL_SLIPPERY;
4445 case 0x15a8: // wall (not round)
4449 case 0x15a9: // (blue)
4450 element = EL_CHAR_A;
4453 case 0x15aa: // (blue)
4454 element = EL_CHAR_B;
4457 case 0x15ab: // (blue)
4458 element = EL_CHAR_C;
4461 case 0x15ac: // (blue)
4462 element = EL_CHAR_D;
4465 case 0x15ad: // (blue)
4466 element = EL_CHAR_E;
4469 case 0x15ae: // (blue)
4470 element = EL_CHAR_F;
4473 case 0x15af: // (blue)
4474 element = EL_CHAR_G;
4477 case 0x15b0: // (blue)
4478 element = EL_CHAR_H;
4481 case 0x15b1: // (blue)
4482 element = EL_CHAR_I;
4485 case 0x15b2: // (blue)
4486 element = EL_CHAR_J;
4489 case 0x15b3: // (blue)
4490 element = EL_CHAR_K;
4493 case 0x15b4: // (blue)
4494 element = EL_CHAR_L;
4497 case 0x15b5: // (blue)
4498 element = EL_CHAR_M;
4501 case 0x15b6: // (blue)
4502 element = EL_CHAR_N;
4505 case 0x15b7: // (blue)
4506 element = EL_CHAR_O;
4509 case 0x15b8: // (blue)
4510 element = EL_CHAR_P;
4513 case 0x15b9: // (blue)
4514 element = EL_CHAR_Q;
4517 case 0x15ba: // (blue)
4518 element = EL_CHAR_R;
4521 case 0x15bb: // (blue)
4522 element = EL_CHAR_S;
4525 case 0x15bc: // (blue)
4526 element = EL_CHAR_T;
4529 case 0x15bd: // (blue)
4530 element = EL_CHAR_U;
4533 case 0x15be: // (blue)
4534 element = EL_CHAR_V;
4537 case 0x15bf: // (blue)
4538 element = EL_CHAR_W;
4541 case 0x15c0: // (blue)
4542 element = EL_CHAR_X;
4545 case 0x15c1: // (blue)
4546 element = EL_CHAR_Y;
4549 case 0x15c2: // (blue)
4550 element = EL_CHAR_Z;
4553 case 0x15c3: // (blue)
4554 element = EL_CHAR_AUMLAUT;
4557 case 0x15c4: // (blue)
4558 element = EL_CHAR_OUMLAUT;
4561 case 0x15c5: // (blue)
4562 element = EL_CHAR_UUMLAUT;
4565 case 0x15c6: // (blue)
4566 element = EL_CHAR_0;
4569 case 0x15c7: // (blue)
4570 element = EL_CHAR_1;
4573 case 0x15c8: // (blue)
4574 element = EL_CHAR_2;
4577 case 0x15c9: // (blue)
4578 element = EL_CHAR_3;
4581 case 0x15ca: // (blue)
4582 element = EL_CHAR_4;
4585 case 0x15cb: // (blue)
4586 element = EL_CHAR_5;
4589 case 0x15cc: // (blue)
4590 element = EL_CHAR_6;
4593 case 0x15cd: // (blue)
4594 element = EL_CHAR_7;
4597 case 0x15ce: // (blue)
4598 element = EL_CHAR_8;
4601 case 0x15cf: // (blue)
4602 element = EL_CHAR_9;
4605 case 0x15d0: // (blue)
4606 element = EL_CHAR_PERIOD;
4609 case 0x15d1: // (blue)
4610 element = EL_CHAR_EXCLAM;
4613 case 0x15d2: // (blue)
4614 element = EL_CHAR_COLON;
4617 case 0x15d3: // (blue)
4618 element = EL_CHAR_LESS;
4621 case 0x15d4: // (blue)
4622 element = EL_CHAR_GREATER;
4625 case 0x15d5: // (blue)
4626 element = EL_CHAR_QUESTION;
4629 case 0x15d6: // (blue)
4630 element = EL_CHAR_COPYRIGHT;
4633 case 0x15d7: // (blue)
4634 element = EL_CHAR_UP;
4637 case 0x15d8: // (blue)
4638 element = EL_CHAR_DOWN;
4641 case 0x15d9: // (blue)
4642 element = EL_CHAR_BUTTON;
4645 case 0x15da: // (blue)
4646 element = EL_CHAR_PLUS;
4649 case 0x15db: // (blue)
4650 element = EL_CHAR_MINUS;
4653 case 0x15dc: // (blue)
4654 element = EL_CHAR_APOSTROPHE;
4657 case 0x15dd: // (blue)
4658 element = EL_CHAR_PARENLEFT;
4661 case 0x15de: // (blue)
4662 element = EL_CHAR_PARENRIGHT;
4665 case 0x15df: // (green)
4666 element = EL_CHAR_A;
4669 case 0x15e0: // (green)
4670 element = EL_CHAR_B;
4673 case 0x15e1: // (green)
4674 element = EL_CHAR_C;
4677 case 0x15e2: // (green)
4678 element = EL_CHAR_D;
4681 case 0x15e3: // (green)
4682 element = EL_CHAR_E;
4685 case 0x15e4: // (green)
4686 element = EL_CHAR_F;
4689 case 0x15e5: // (green)
4690 element = EL_CHAR_G;
4693 case 0x15e6: // (green)
4694 element = EL_CHAR_H;
4697 case 0x15e7: // (green)
4698 element = EL_CHAR_I;
4701 case 0x15e8: // (green)
4702 element = EL_CHAR_J;
4705 case 0x15e9: // (green)
4706 element = EL_CHAR_K;
4709 case 0x15ea: // (green)
4710 element = EL_CHAR_L;
4713 case 0x15eb: // (green)
4714 element = EL_CHAR_M;
4717 case 0x15ec: // (green)
4718 element = EL_CHAR_N;
4721 case 0x15ed: // (green)
4722 element = EL_CHAR_O;
4725 case 0x15ee: // (green)
4726 element = EL_CHAR_P;
4729 case 0x15ef: // (green)
4730 element = EL_CHAR_Q;
4733 case 0x15f0: // (green)
4734 element = EL_CHAR_R;
4737 case 0x15f1: // (green)
4738 element = EL_CHAR_S;
4741 case 0x15f2: // (green)
4742 element = EL_CHAR_T;
4745 case 0x15f3: // (green)
4746 element = EL_CHAR_U;
4749 case 0x15f4: // (green)
4750 element = EL_CHAR_V;
4753 case 0x15f5: // (green)
4754 element = EL_CHAR_W;
4757 case 0x15f6: // (green)
4758 element = EL_CHAR_X;
4761 case 0x15f7: // (green)
4762 element = EL_CHAR_Y;
4765 case 0x15f8: // (green)
4766 element = EL_CHAR_Z;
4769 case 0x15f9: // (green)
4770 element = EL_CHAR_AUMLAUT;
4773 case 0x15fa: // (green)
4774 element = EL_CHAR_OUMLAUT;
4777 case 0x15fb: // (green)
4778 element = EL_CHAR_UUMLAUT;
4781 case 0x15fc: // (green)
4782 element = EL_CHAR_0;
4785 case 0x15fd: // (green)
4786 element = EL_CHAR_1;
4789 case 0x15fe: // (green)
4790 element = EL_CHAR_2;
4793 case 0x15ff: // (green)
4794 element = EL_CHAR_3;
4797 case 0x1600: // (green)
4798 element = EL_CHAR_4;
4801 case 0x1601: // (green)
4802 element = EL_CHAR_5;
4805 case 0x1602: // (green)
4806 element = EL_CHAR_6;
4809 case 0x1603: // (green)
4810 element = EL_CHAR_7;
4813 case 0x1604: // (green)
4814 element = EL_CHAR_8;
4817 case 0x1605: // (green)
4818 element = EL_CHAR_9;
4821 case 0x1606: // (green)
4822 element = EL_CHAR_PERIOD;
4825 case 0x1607: // (green)
4826 element = EL_CHAR_EXCLAM;
4829 case 0x1608: // (green)
4830 element = EL_CHAR_COLON;
4833 case 0x1609: // (green)
4834 element = EL_CHAR_LESS;
4837 case 0x160a: // (green)
4838 element = EL_CHAR_GREATER;
4841 case 0x160b: // (green)
4842 element = EL_CHAR_QUESTION;
4845 case 0x160c: // (green)
4846 element = EL_CHAR_COPYRIGHT;
4849 case 0x160d: // (green)
4850 element = EL_CHAR_UP;
4853 case 0x160e: // (green)
4854 element = EL_CHAR_DOWN;
4857 case 0x160f: // (green)
4858 element = EL_CHAR_BUTTON;
4861 case 0x1610: // (green)
4862 element = EL_CHAR_PLUS;
4865 case 0x1611: // (green)
4866 element = EL_CHAR_MINUS;
4869 case 0x1612: // (green)
4870 element = EL_CHAR_APOSTROPHE;
4873 case 0x1613: // (green)
4874 element = EL_CHAR_PARENLEFT;
4877 case 0x1614: // (green)
4878 element = EL_CHAR_PARENRIGHT;
4881 case 0x1615: // (blue steel)
4882 element = EL_STEEL_CHAR_A;
4885 case 0x1616: // (blue steel)
4886 element = EL_STEEL_CHAR_B;
4889 case 0x1617: // (blue steel)
4890 element = EL_STEEL_CHAR_C;
4893 case 0x1618: // (blue steel)
4894 element = EL_STEEL_CHAR_D;
4897 case 0x1619: // (blue steel)
4898 element = EL_STEEL_CHAR_E;
4901 case 0x161a: // (blue steel)
4902 element = EL_STEEL_CHAR_F;
4905 case 0x161b: // (blue steel)
4906 element = EL_STEEL_CHAR_G;
4909 case 0x161c: // (blue steel)
4910 element = EL_STEEL_CHAR_H;
4913 case 0x161d: // (blue steel)
4914 element = EL_STEEL_CHAR_I;
4917 case 0x161e: // (blue steel)
4918 element = EL_STEEL_CHAR_J;
4921 case 0x161f: // (blue steel)
4922 element = EL_STEEL_CHAR_K;
4925 case 0x1620: // (blue steel)
4926 element = EL_STEEL_CHAR_L;
4929 case 0x1621: // (blue steel)
4930 element = EL_STEEL_CHAR_M;
4933 case 0x1622: // (blue steel)
4934 element = EL_STEEL_CHAR_N;
4937 case 0x1623: // (blue steel)
4938 element = EL_STEEL_CHAR_O;
4941 case 0x1624: // (blue steel)
4942 element = EL_STEEL_CHAR_P;
4945 case 0x1625: // (blue steel)
4946 element = EL_STEEL_CHAR_Q;
4949 case 0x1626: // (blue steel)
4950 element = EL_STEEL_CHAR_R;
4953 case 0x1627: // (blue steel)
4954 element = EL_STEEL_CHAR_S;
4957 case 0x1628: // (blue steel)
4958 element = EL_STEEL_CHAR_T;
4961 case 0x1629: // (blue steel)
4962 element = EL_STEEL_CHAR_U;
4965 case 0x162a: // (blue steel)
4966 element = EL_STEEL_CHAR_V;
4969 case 0x162b: // (blue steel)
4970 element = EL_STEEL_CHAR_W;
4973 case 0x162c: // (blue steel)
4974 element = EL_STEEL_CHAR_X;
4977 case 0x162d: // (blue steel)
4978 element = EL_STEEL_CHAR_Y;
4981 case 0x162e: // (blue steel)
4982 element = EL_STEEL_CHAR_Z;
4985 case 0x162f: // (blue steel)
4986 element = EL_STEEL_CHAR_AUMLAUT;
4989 case 0x1630: // (blue steel)
4990 element = EL_STEEL_CHAR_OUMLAUT;
4993 case 0x1631: // (blue steel)
4994 element = EL_STEEL_CHAR_UUMLAUT;
4997 case 0x1632: // (blue steel)
4998 element = EL_STEEL_CHAR_0;
5001 case 0x1633: // (blue steel)
5002 element = EL_STEEL_CHAR_1;
5005 case 0x1634: // (blue steel)
5006 element = EL_STEEL_CHAR_2;
5009 case 0x1635: // (blue steel)
5010 element = EL_STEEL_CHAR_3;
5013 case 0x1636: // (blue steel)
5014 element = EL_STEEL_CHAR_4;
5017 case 0x1637: // (blue steel)
5018 element = EL_STEEL_CHAR_5;
5021 case 0x1638: // (blue steel)
5022 element = EL_STEEL_CHAR_6;
5025 case 0x1639: // (blue steel)
5026 element = EL_STEEL_CHAR_7;
5029 case 0x163a: // (blue steel)
5030 element = EL_STEEL_CHAR_8;
5033 case 0x163b: // (blue steel)
5034 element = EL_STEEL_CHAR_9;
5037 case 0x163c: // (blue steel)
5038 element = EL_STEEL_CHAR_PERIOD;
5041 case 0x163d: // (blue steel)
5042 element = EL_STEEL_CHAR_EXCLAM;
5045 case 0x163e: // (blue steel)
5046 element = EL_STEEL_CHAR_COLON;
5049 case 0x163f: // (blue steel)
5050 element = EL_STEEL_CHAR_LESS;
5053 case 0x1640: // (blue steel)
5054 element = EL_STEEL_CHAR_GREATER;
5057 case 0x1641: // (blue steel)
5058 element = EL_STEEL_CHAR_QUESTION;
5061 case 0x1642: // (blue steel)
5062 element = EL_STEEL_CHAR_COPYRIGHT;
5065 case 0x1643: // (blue steel)
5066 element = EL_STEEL_CHAR_UP;
5069 case 0x1644: // (blue steel)
5070 element = EL_STEEL_CHAR_DOWN;
5073 case 0x1645: // (blue steel)
5074 element = EL_STEEL_CHAR_BUTTON;
5077 case 0x1646: // (blue steel)
5078 element = EL_STEEL_CHAR_PLUS;
5081 case 0x1647: // (blue steel)
5082 element = EL_STEEL_CHAR_MINUS;
5085 case 0x1648: // (blue steel)
5086 element = EL_STEEL_CHAR_APOSTROPHE;
5089 case 0x1649: // (blue steel)
5090 element = EL_STEEL_CHAR_PARENLEFT;
5093 case 0x164a: // (blue steel)
5094 element = EL_STEEL_CHAR_PARENRIGHT;
5097 case 0x164b: // (green steel)
5098 element = EL_STEEL_CHAR_A;
5101 case 0x164c: // (green steel)
5102 element = EL_STEEL_CHAR_B;
5105 case 0x164d: // (green steel)
5106 element = EL_STEEL_CHAR_C;
5109 case 0x164e: // (green steel)
5110 element = EL_STEEL_CHAR_D;
5113 case 0x164f: // (green steel)
5114 element = EL_STEEL_CHAR_E;
5117 case 0x1650: // (green steel)
5118 element = EL_STEEL_CHAR_F;
5121 case 0x1651: // (green steel)
5122 element = EL_STEEL_CHAR_G;
5125 case 0x1652: // (green steel)
5126 element = EL_STEEL_CHAR_H;
5129 case 0x1653: // (green steel)
5130 element = EL_STEEL_CHAR_I;
5133 case 0x1654: // (green steel)
5134 element = EL_STEEL_CHAR_J;
5137 case 0x1655: // (green steel)
5138 element = EL_STEEL_CHAR_K;
5141 case 0x1656: // (green steel)
5142 element = EL_STEEL_CHAR_L;
5145 case 0x1657: // (green steel)
5146 element = EL_STEEL_CHAR_M;
5149 case 0x1658: // (green steel)
5150 element = EL_STEEL_CHAR_N;
5153 case 0x1659: // (green steel)
5154 element = EL_STEEL_CHAR_O;
5157 case 0x165a: // (green steel)
5158 element = EL_STEEL_CHAR_P;
5161 case 0x165b: // (green steel)
5162 element = EL_STEEL_CHAR_Q;
5165 case 0x165c: // (green steel)
5166 element = EL_STEEL_CHAR_R;
5169 case 0x165d: // (green steel)
5170 element = EL_STEEL_CHAR_S;
5173 case 0x165e: // (green steel)
5174 element = EL_STEEL_CHAR_T;
5177 case 0x165f: // (green steel)
5178 element = EL_STEEL_CHAR_U;
5181 case 0x1660: // (green steel)
5182 element = EL_STEEL_CHAR_V;
5185 case 0x1661: // (green steel)
5186 element = EL_STEEL_CHAR_W;
5189 case 0x1662: // (green steel)
5190 element = EL_STEEL_CHAR_X;
5193 case 0x1663: // (green steel)
5194 element = EL_STEEL_CHAR_Y;
5197 case 0x1664: // (green steel)
5198 element = EL_STEEL_CHAR_Z;
5201 case 0x1665: // (green steel)
5202 element = EL_STEEL_CHAR_AUMLAUT;
5205 case 0x1666: // (green steel)
5206 element = EL_STEEL_CHAR_OUMLAUT;
5209 case 0x1667: // (green steel)
5210 element = EL_STEEL_CHAR_UUMLAUT;
5213 case 0x1668: // (green steel)
5214 element = EL_STEEL_CHAR_0;
5217 case 0x1669: // (green steel)
5218 element = EL_STEEL_CHAR_1;
5221 case 0x166a: // (green steel)
5222 element = EL_STEEL_CHAR_2;
5225 case 0x166b: // (green steel)
5226 element = EL_STEEL_CHAR_3;
5229 case 0x166c: // (green steel)
5230 element = EL_STEEL_CHAR_4;
5233 case 0x166d: // (green steel)
5234 element = EL_STEEL_CHAR_5;
5237 case 0x166e: // (green steel)
5238 element = EL_STEEL_CHAR_6;
5241 case 0x166f: // (green steel)
5242 element = EL_STEEL_CHAR_7;
5245 case 0x1670: // (green steel)
5246 element = EL_STEEL_CHAR_8;
5249 case 0x1671: // (green steel)
5250 element = EL_STEEL_CHAR_9;
5253 case 0x1672: // (green steel)
5254 element = EL_STEEL_CHAR_PERIOD;
5257 case 0x1673: // (green steel)
5258 element = EL_STEEL_CHAR_EXCLAM;
5261 case 0x1674: // (green steel)
5262 element = EL_STEEL_CHAR_COLON;
5265 case 0x1675: // (green steel)
5266 element = EL_STEEL_CHAR_LESS;
5269 case 0x1676: // (green steel)
5270 element = EL_STEEL_CHAR_GREATER;
5273 case 0x1677: // (green steel)
5274 element = EL_STEEL_CHAR_QUESTION;
5277 case 0x1678: // (green steel)
5278 element = EL_STEEL_CHAR_COPYRIGHT;
5281 case 0x1679: // (green steel)
5282 element = EL_STEEL_CHAR_UP;
5285 case 0x167a: // (green steel)
5286 element = EL_STEEL_CHAR_DOWN;
5289 case 0x167b: // (green steel)
5290 element = EL_STEEL_CHAR_BUTTON;
5293 case 0x167c: // (green steel)
5294 element = EL_STEEL_CHAR_PLUS;
5297 case 0x167d: // (green steel)
5298 element = EL_STEEL_CHAR_MINUS;
5301 case 0x167e: // (green steel)
5302 element = EL_STEEL_CHAR_APOSTROPHE;
5305 case 0x167f: // (green steel)
5306 element = EL_STEEL_CHAR_PARENLEFT;
5309 case 0x1680: // (green steel)
5310 element = EL_STEEL_CHAR_PARENRIGHT;
5313 case 0x1681: // gate (red)
5314 element = EL_EM_GATE_1;
5317 case 0x1682: // secret gate (red)
5318 element = EL_EM_GATE_1_GRAY;
5321 case 0x1683: // gate (yellow)
5322 element = EL_EM_GATE_2;
5325 case 0x1684: // secret gate (yellow)
5326 element = EL_EM_GATE_2_GRAY;
5329 case 0x1685: // gate (blue)
5330 element = EL_EM_GATE_4;
5333 case 0x1686: // secret gate (blue)
5334 element = EL_EM_GATE_4_GRAY;
5337 case 0x1687: // gate (green)
5338 element = EL_EM_GATE_3;
5341 case 0x1688: // secret gate (green)
5342 element = EL_EM_GATE_3_GRAY;
5345 case 0x1689: // gate (white)
5346 element = EL_DC_GATE_WHITE;
5349 case 0x168a: // secret gate (white)
5350 element = EL_DC_GATE_WHITE_GRAY;
5353 case 0x168b: // secret gate (no key)
5354 element = EL_DC_GATE_FAKE_GRAY;
5358 element = EL_ROBOT_WHEEL;
5362 element = EL_DC_TIMEGATE_SWITCH;
5366 element = EL_ACID_POOL_BOTTOM;
5370 element = EL_ACID_POOL_TOPLEFT;
5374 element = EL_ACID_POOL_TOPRIGHT;
5378 element = EL_ACID_POOL_BOTTOMLEFT;
5382 element = EL_ACID_POOL_BOTTOMRIGHT;
5386 element = EL_STEELWALL;
5390 element = EL_STEELWALL_SLIPPERY;
5393 case 0x1695: // steel wall (not round)
5394 element = EL_STEELWALL;
5397 case 0x1696: // steel wall (left)
5398 element = EL_DC_STEELWALL_1_LEFT;
5401 case 0x1697: // steel wall (bottom)
5402 element = EL_DC_STEELWALL_1_BOTTOM;
5405 case 0x1698: // steel wall (right)
5406 element = EL_DC_STEELWALL_1_RIGHT;
5409 case 0x1699: // steel wall (top)
5410 element = EL_DC_STEELWALL_1_TOP;
5413 case 0x169a: // steel wall (left/bottom)
5414 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5417 case 0x169b: // steel wall (right/bottom)
5418 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5421 case 0x169c: // steel wall (right/top)
5422 element = EL_DC_STEELWALL_1_TOPRIGHT;
5425 case 0x169d: // steel wall (left/top)
5426 element = EL_DC_STEELWALL_1_TOPLEFT;
5429 case 0x169e: // steel wall (right/bottom small)
5430 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5433 case 0x169f: // steel wall (left/bottom small)
5434 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5437 case 0x16a0: // steel wall (right/top small)
5438 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5441 case 0x16a1: // steel wall (left/top small)
5442 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5445 case 0x16a2: // steel wall (left/right)
5446 element = EL_DC_STEELWALL_1_VERTICAL;
5449 case 0x16a3: // steel wall (top/bottom)
5450 element = EL_DC_STEELWALL_1_HORIZONTAL;
5453 case 0x16a4: // steel wall 2 (left end)
5454 element = EL_DC_STEELWALL_2_LEFT;
5457 case 0x16a5: // steel wall 2 (right end)
5458 element = EL_DC_STEELWALL_2_RIGHT;
5461 case 0x16a6: // steel wall 2 (top end)
5462 element = EL_DC_STEELWALL_2_TOP;
5465 case 0x16a7: // steel wall 2 (bottom end)
5466 element = EL_DC_STEELWALL_2_BOTTOM;
5469 case 0x16a8: // steel wall 2 (left/right)
5470 element = EL_DC_STEELWALL_2_HORIZONTAL;
5473 case 0x16a9: // steel wall 2 (up/down)
5474 element = EL_DC_STEELWALL_2_VERTICAL;
5477 case 0x16aa: // steel wall 2 (mid)
5478 element = EL_DC_STEELWALL_2_MIDDLE;
5482 element = EL_SIGN_EXCLAMATION;
5486 element = EL_SIGN_RADIOACTIVITY;
5490 element = EL_SIGN_STOP;
5494 element = EL_SIGN_WHEELCHAIR;
5498 element = EL_SIGN_PARKING;
5502 element = EL_SIGN_NO_ENTRY;
5506 element = EL_SIGN_HEART;
5510 element = EL_SIGN_GIVE_WAY;
5514 element = EL_SIGN_ENTRY_FORBIDDEN;
5518 element = EL_SIGN_EMERGENCY_EXIT;
5522 element = EL_SIGN_YIN_YANG;
5526 element = EL_WALL_EMERALD;
5530 element = EL_WALL_DIAMOND;
5534 element = EL_WALL_PEARL;
5538 element = EL_WALL_CRYSTAL;
5542 element = EL_INVISIBLE_WALL;
5546 element = EL_INVISIBLE_STEELWALL;
5550 // EL_INVISIBLE_SAND
5553 element = EL_LIGHT_SWITCH;
5557 element = EL_ENVELOPE_1;
5561 if (element >= 0x0117 && element <= 0x036e) // (?)
5562 element = EL_DIAMOND;
5563 else if (element >= 0x042d && element <= 0x0684) // (?)
5564 element = EL_EMERALD;
5565 else if (element >= 0x157c && element <= 0x158b)
5567 else if (element >= 0x1590 && element <= 0x159f)
5568 element = EL_DC_LANDMINE;
5569 else if (element >= 0x16bc && element <= 0x16cb)
5570 element = EL_INVISIBLE_SAND;
5573 Warn("unknown Diamond Caves element 0x%04x", element);
5575 element = EL_UNKNOWN;
5580 return getMappedElement(element);
5583 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5586 byte header[DC_LEVEL_HEADER_SIZE];
5588 int envelope_header_pos = 62;
5589 int envelope_content_pos = 94;
5590 int level_name_pos = 251;
5591 int level_author_pos = 292;
5592 int envelope_header_len;
5593 int envelope_content_len;
5595 int level_author_len;
5597 int num_yamyam_contents;
5600 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5602 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5604 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5606 header[i * 2 + 0] = header_word >> 8;
5607 header[i * 2 + 1] = header_word & 0xff;
5610 // read some values from level header to check level decoding integrity
5611 fieldx = header[6] | (header[7] << 8);
5612 fieldy = header[8] | (header[9] << 8);
5613 num_yamyam_contents = header[60] | (header[61] << 8);
5615 // do some simple sanity checks to ensure that level was correctly decoded
5616 if (fieldx < 1 || fieldx > 256 ||
5617 fieldy < 1 || fieldy > 256 ||
5618 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5620 level->no_valid_file = TRUE;
5622 Warn("cannot decode level from stream -- using empty level");
5627 // maximum envelope header size is 31 bytes
5628 envelope_header_len = header[envelope_header_pos];
5629 // maximum envelope content size is 110 (156?) bytes
5630 envelope_content_len = header[envelope_content_pos];
5632 // maximum level title size is 40 bytes
5633 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5634 // maximum level author size is 30 (51?) bytes
5635 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5639 for (i = 0; i < envelope_header_len; i++)
5640 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5641 level->envelope[0].text[envelope_size++] =
5642 header[envelope_header_pos + 1 + i];
5644 if (envelope_header_len > 0 && envelope_content_len > 0)
5646 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5647 level->envelope[0].text[envelope_size++] = '\n';
5648 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5649 level->envelope[0].text[envelope_size++] = '\n';
5652 for (i = 0; i < envelope_content_len; i++)
5653 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5654 level->envelope[0].text[envelope_size++] =
5655 header[envelope_content_pos + 1 + i];
5657 level->envelope[0].text[envelope_size] = '\0';
5659 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5660 level->envelope[0].ysize = 10;
5661 level->envelope[0].autowrap = TRUE;
5662 level->envelope[0].centered = TRUE;
5664 for (i = 0; i < level_name_len; i++)
5665 level->name[i] = header[level_name_pos + 1 + i];
5666 level->name[level_name_len] = '\0';
5668 for (i = 0; i < level_author_len; i++)
5669 level->author[i] = header[level_author_pos + 1 + i];
5670 level->author[level_author_len] = '\0';
5672 num_yamyam_contents = header[60] | (header[61] << 8);
5673 level->num_yamyam_contents =
5674 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5676 for (i = 0; i < num_yamyam_contents; i++)
5678 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5680 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5681 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5683 if (i < MAX_ELEMENT_CONTENTS)
5684 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5688 fieldx = header[6] | (header[7] << 8);
5689 fieldy = header[8] | (header[9] << 8);
5690 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5691 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5693 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5695 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5696 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5698 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5699 level->field[x][y] = getMappedElement_DC(element_dc);
5702 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5703 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5704 level->field[x][y] = EL_PLAYER_1;
5706 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5707 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5708 level->field[x][y] = EL_PLAYER_2;
5710 level->gems_needed = header[18] | (header[19] << 8);
5712 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5713 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5714 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5715 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5716 level->score[SC_NUT] = header[28] | (header[29] << 8);
5717 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5718 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5719 level->score[SC_BUG] = header[34] | (header[35] << 8);
5720 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5721 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5722 level->score[SC_KEY] = header[40] | (header[41] << 8);
5723 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5725 level->time = header[44] | (header[45] << 8);
5727 level->amoeba_speed = header[46] | (header[47] << 8);
5728 level->time_light = header[48] | (header[49] << 8);
5729 level->time_timegate = header[50] | (header[51] << 8);
5730 level->time_wheel = header[52] | (header[53] << 8);
5731 level->time_magic_wall = header[54] | (header[55] << 8);
5732 level->extra_time = header[56] | (header[57] << 8);
5733 level->shield_normal_time = header[58] | (header[59] << 8);
5735 // shield and extra time elements do not have a score
5736 level->score[SC_SHIELD] = 0;
5737 level->extra_time_score = 0;
5739 // set time for normal and deadly shields to the same value
5740 level->shield_deadly_time = level->shield_normal_time;
5742 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5743 // can slip down from flat walls, like normal walls and steel walls
5744 level->em_slippery_gems = TRUE;
5746 // time score is counted for each 10 seconds left in Diamond Caves levels
5747 level->time_score_base = 10;
5750 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5751 struct LevelFileInfo *level_file_info,
5752 boolean level_info_only)
5754 char *filename = level_file_info->filename;
5756 int num_magic_bytes = 8;
5757 char magic_bytes[num_magic_bytes + 1];
5758 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5760 if (!(file = openFile(filename, MODE_READ)))
5762 level->no_valid_file = TRUE;
5764 if (!level_info_only)
5765 Warn("cannot read level '%s' -- using empty level", filename);
5770 // fseek(file, 0x0000, SEEK_SET);
5772 if (level_file_info->packed)
5774 // read "magic bytes" from start of file
5775 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5776 magic_bytes[0] = '\0';
5778 // check "magic bytes" for correct file format
5779 if (!strPrefix(magic_bytes, "DC2"))
5781 level->no_valid_file = TRUE;
5783 Warn("unknown DC level file '%s' -- using empty level", filename);
5788 if (strPrefix(magic_bytes, "DC2Win95") ||
5789 strPrefix(magic_bytes, "DC2Win98"))
5791 int position_first_level = 0x00fa;
5792 int extra_bytes = 4;
5795 // advance file stream to first level inside the level package
5796 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5798 // each block of level data is followed by block of non-level data
5799 num_levels_to_skip *= 2;
5801 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5802 while (num_levels_to_skip >= 0)
5804 // advance file stream to next level inside the level package
5805 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5807 level->no_valid_file = TRUE;
5809 Warn("cannot fseek in file '%s' -- using empty level", filename);
5814 // skip apparently unused extra bytes following each level
5815 ReadUnusedBytesFromFile(file, extra_bytes);
5817 // read size of next level in level package
5818 skip_bytes = getFile32BitLE(file);
5820 num_levels_to_skip--;
5825 level->no_valid_file = TRUE;
5827 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5833 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5839 // ----------------------------------------------------------------------------
5840 // functions for loading SB level
5841 // ----------------------------------------------------------------------------
5843 int getMappedElement_SB(int element_ascii, boolean use_ces)
5851 sb_element_mapping[] =
5853 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5854 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5855 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5856 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5857 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5858 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5859 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5860 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5867 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5868 if (element_ascii == sb_element_mapping[i].ascii)
5869 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5871 return EL_UNDEFINED;
5874 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5875 struct LevelFileInfo *level_file_info,
5876 boolean level_info_only)
5878 char *filename = level_file_info->filename;
5879 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5880 char last_comment[MAX_LINE_LEN];
5881 char level_name[MAX_LINE_LEN];
5884 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5885 boolean read_continued_line = FALSE;
5886 boolean reading_playfield = FALSE;
5887 boolean got_valid_playfield_line = FALSE;
5888 boolean invalid_playfield_char = FALSE;
5889 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5890 int file_level_nr = 0;
5892 int x = 0, y = 0; // initialized to make compilers happy
5894 last_comment[0] = '\0';
5895 level_name[0] = '\0';
5897 if (!(file = openFile(filename, MODE_READ)))
5899 level->no_valid_file = TRUE;
5901 if (!level_info_only)
5902 Warn("cannot read level '%s' -- using empty level", filename);
5907 while (!checkEndOfFile(file))
5909 // level successfully read, but next level may follow here
5910 if (!got_valid_playfield_line && reading_playfield)
5912 // read playfield from single level file -- skip remaining file
5913 if (!level_file_info->packed)
5916 if (file_level_nr >= num_levels_to_skip)
5921 last_comment[0] = '\0';
5922 level_name[0] = '\0';
5924 reading_playfield = FALSE;
5927 got_valid_playfield_line = FALSE;
5929 // read next line of input file
5930 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5933 // check if line was completely read and is terminated by line break
5934 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5937 // cut trailing line break (this can be newline and/or carriage return)
5938 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5939 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5942 // copy raw input line for later use (mainly debugging output)
5943 strcpy(line_raw, line);
5945 if (read_continued_line)
5947 // append new line to existing line, if there is enough space
5948 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5949 strcat(previous_line, line_ptr);
5951 strcpy(line, previous_line); // copy storage buffer to line
5953 read_continued_line = FALSE;
5956 // if the last character is '\', continue at next line
5957 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5959 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5960 strcpy(previous_line, line); // copy line to storage buffer
5962 read_continued_line = TRUE;
5968 if (line[0] == '\0')
5971 // extract comment text from comment line
5974 for (line_ptr = line; *line_ptr; line_ptr++)
5975 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5978 strcpy(last_comment, line_ptr);
5983 // extract level title text from line containing level title
5984 if (line[0] == '\'')
5986 strcpy(level_name, &line[1]);
5988 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5989 level_name[strlen(level_name) - 1] = '\0';
5994 // skip lines containing only spaces (or empty lines)
5995 for (line_ptr = line; *line_ptr; line_ptr++)
5996 if (*line_ptr != ' ')
5998 if (*line_ptr == '\0')
6001 // at this point, we have found a line containing part of a playfield
6003 got_valid_playfield_line = TRUE;
6005 if (!reading_playfield)
6007 reading_playfield = TRUE;
6008 invalid_playfield_char = FALSE;
6010 for (x = 0; x < MAX_LEV_FIELDX; x++)
6011 for (y = 0; y < MAX_LEV_FIELDY; y++)
6012 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6017 // start with topmost tile row
6021 // skip playfield line if larger row than allowed
6022 if (y >= MAX_LEV_FIELDY)
6025 // start with leftmost tile column
6028 // read playfield elements from line
6029 for (line_ptr = line; *line_ptr; line_ptr++)
6031 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6033 // stop parsing playfield line if larger column than allowed
6034 if (x >= MAX_LEV_FIELDX)
6037 if (mapped_sb_element == EL_UNDEFINED)
6039 invalid_playfield_char = TRUE;
6044 level->field[x][y] = mapped_sb_element;
6046 // continue with next tile column
6049 level->fieldx = MAX(x, level->fieldx);
6052 if (invalid_playfield_char)
6054 // if first playfield line, treat invalid lines as comment lines
6056 reading_playfield = FALSE;
6061 // continue with next tile row
6069 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6070 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6072 if (!reading_playfield)
6074 level->no_valid_file = TRUE;
6076 Warn("cannot read level '%s' -- using empty level", filename);
6081 if (*level_name != '\0')
6083 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6084 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6086 else if (*last_comment != '\0')
6088 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6089 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6093 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6096 // set all empty fields beyond the border walls to invisible steel wall
6097 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6099 if ((x == 0 || x == level->fieldx - 1 ||
6100 y == 0 || y == level->fieldy - 1) &&
6101 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6102 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6103 level->field, level->fieldx, level->fieldy);
6106 // set special level settings for Sokoban levels
6109 level->use_step_counter = TRUE;
6111 if (load_xsb_to_ces)
6113 // special global settings can now be set in level template
6115 level->use_custom_template = TRUE;
6120 // -------------------------------------------------------------------------
6121 // functions for handling native levels
6122 // -------------------------------------------------------------------------
6124 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6125 struct LevelFileInfo *level_file_info,
6126 boolean level_info_only)
6128 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6129 level->no_valid_file = TRUE;
6132 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6133 struct LevelFileInfo *level_file_info,
6134 boolean level_info_only)
6138 // determine position of requested level inside level package
6139 if (level_file_info->packed)
6140 pos = level_file_info->nr - leveldir_current->first_level;
6142 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6143 level->no_valid_file = TRUE;
6146 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6147 struct LevelFileInfo *level_file_info,
6148 boolean level_info_only)
6150 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6151 level->no_valid_file = TRUE;
6154 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6156 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6157 CopyNativeLevel_RND_to_EM(level);
6158 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6159 CopyNativeLevel_RND_to_SP(level);
6160 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6161 CopyNativeLevel_RND_to_MM(level);
6164 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6166 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6167 CopyNativeLevel_EM_to_RND(level);
6168 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6169 CopyNativeLevel_SP_to_RND(level);
6170 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6171 CopyNativeLevel_MM_to_RND(level);
6174 void SaveNativeLevel(struct LevelInfo *level)
6176 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6178 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6179 char *filename = getLevelFilenameFromBasename(basename);
6181 CopyNativeLevel_RND_to_SP(level);
6182 CopyNativeTape_RND_to_SP(level);
6184 SaveNativeLevel_SP(filename);
6189 // ----------------------------------------------------------------------------
6190 // functions for loading generic level
6191 // ----------------------------------------------------------------------------
6193 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6194 struct LevelFileInfo *level_file_info,
6195 boolean level_info_only)
6197 // always start with reliable default values
6198 setLevelInfoToDefaults(level, level_info_only, TRUE);
6200 switch (level_file_info->type)
6202 case LEVEL_FILE_TYPE_RND:
6203 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6206 case LEVEL_FILE_TYPE_EM:
6207 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6208 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6211 case LEVEL_FILE_TYPE_SP:
6212 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6213 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6216 case LEVEL_FILE_TYPE_MM:
6217 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6218 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6221 case LEVEL_FILE_TYPE_DC:
6222 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6225 case LEVEL_FILE_TYPE_SB:
6226 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6230 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6234 // if level file is invalid, restore level structure to default values
6235 if (level->no_valid_file)
6236 setLevelInfoToDefaults(level, level_info_only, FALSE);
6238 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6239 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6241 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6242 CopyNativeLevel_Native_to_RND(level);
6245 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6247 static struct LevelFileInfo level_file_info;
6249 // always start with reliable default values
6250 setFileInfoToDefaults(&level_file_info);
6252 level_file_info.nr = 0; // unknown level number
6253 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6255 setString(&level_file_info.filename, filename);
6257 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6260 static void LoadLevel_InitVersion(struct LevelInfo *level)
6264 if (leveldir_current == NULL) // only when dumping level
6267 // all engine modifications also valid for levels which use latest engine
6268 if (level->game_version < VERSION_IDENT(3,2,0,5))
6270 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6271 level->time_score_base = 10;
6274 if (leveldir_current->latest_engine)
6276 // ---------- use latest game engine --------------------------------------
6278 /* For all levels which are forced to use the latest game engine version
6279 (normally all but user contributed, private and undefined levels), set
6280 the game engine version to the actual version; this allows for actual
6281 corrections in the game engine to take effect for existing, converted
6282 levels (from "classic" or other existing games) to make the emulation
6283 of the corresponding game more accurate, while (hopefully) not breaking
6284 existing levels created from other players. */
6286 level->game_version = GAME_VERSION_ACTUAL;
6288 /* Set special EM style gems behaviour: EM style gems slip down from
6289 normal, steel and growing wall. As this is a more fundamental change,
6290 it seems better to set the default behaviour to "off" (as it is more
6291 natural) and make it configurable in the level editor (as a property
6292 of gem style elements). Already existing converted levels (neither
6293 private nor contributed levels) are changed to the new behaviour. */
6295 if (level->file_version < FILE_VERSION_2_0)
6296 level->em_slippery_gems = TRUE;
6301 // ---------- use game engine the level was created with --------------------
6303 /* For all levels which are not forced to use the latest game engine
6304 version (normally user contributed, private and undefined levels),
6305 use the version of the game engine the levels were created for.
6307 Since 2.0.1, the game engine version is now directly stored
6308 in the level file (chunk "VERS"), so there is no need anymore
6309 to set the game version from the file version (except for old,
6310 pre-2.0 levels, where the game version is still taken from the
6311 file format version used to store the level -- see above). */
6313 // player was faster than enemies in 1.0.0 and before
6314 if (level->file_version == FILE_VERSION_1_0)
6315 for (i = 0; i < MAX_PLAYERS; i++)
6316 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6318 // default behaviour for EM style gems was "slippery" only in 2.0.1
6319 if (level->game_version == VERSION_IDENT(2,0,1,0))
6320 level->em_slippery_gems = TRUE;
6322 // springs could be pushed over pits before (pre-release version) 2.2.0
6323 if (level->game_version < VERSION_IDENT(2,2,0,0))
6324 level->use_spring_bug = TRUE;
6326 if (level->game_version < VERSION_IDENT(3,2,0,5))
6328 // time orb caused limited time in endless time levels before 3.2.0-5
6329 level->use_time_orb_bug = TRUE;
6331 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6332 level->block_snap_field = FALSE;
6334 // extra time score was same value as time left score before 3.2.0-5
6335 level->extra_time_score = level->score[SC_TIME_BONUS];
6338 if (level->game_version < VERSION_IDENT(3,2,0,7))
6340 // default behaviour for snapping was "not continuous" before 3.2.0-7
6341 level->continuous_snapping = FALSE;
6344 // only few elements were able to actively move into acid before 3.1.0
6345 // trigger settings did not exist before 3.1.0; set to default "any"
6346 if (level->game_version < VERSION_IDENT(3,1,0,0))
6348 // correct "can move into acid" settings (all zero in old levels)
6350 level->can_move_into_acid_bits = 0; // nothing can move into acid
6351 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6353 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6354 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6355 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6356 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6358 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6359 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6361 // correct trigger settings (stored as zero == "none" in old levels)
6363 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6365 int element = EL_CUSTOM_START + i;
6366 struct ElementInfo *ei = &element_info[element];
6368 for (j = 0; j < ei->num_change_pages; j++)
6370 struct ElementChangeInfo *change = &ei->change_page[j];
6372 change->trigger_player = CH_PLAYER_ANY;
6373 change->trigger_page = CH_PAGE_ANY;
6378 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6380 int element = EL_CUSTOM_256;
6381 struct ElementInfo *ei = &element_info[element];
6382 struct ElementChangeInfo *change = &ei->change_page[0];
6384 /* This is needed to fix a problem that was caused by a bugfix in function
6385 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6386 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6387 not replace walkable elements, but instead just placed the player on it,
6388 without placing the Sokoban field under the player). Unfortunately, this
6389 breaks "Snake Bite" style levels when the snake is halfway through a door
6390 that just closes (the snake head is still alive and can be moved in this
6391 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6392 player (without Sokoban element) which then gets killed as designed). */
6394 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6395 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6396 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6397 change->target_element = EL_PLAYER_1;
6400 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6401 if (level->game_version < VERSION_IDENT(3,2,5,0))
6403 /* This is needed to fix a problem that was caused by a bugfix in function
6404 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6405 corrects the behaviour when a custom element changes to another custom
6406 element with a higher element number that has change actions defined.
6407 Normally, only one change per frame is allowed for custom elements.
6408 Therefore, it is checked if a custom element already changed in the
6409 current frame; if it did, subsequent changes are suppressed.
6410 Unfortunately, this is only checked for element changes, but not for
6411 change actions, which are still executed. As the function above loops
6412 through all custom elements from lower to higher, an element change
6413 resulting in a lower CE number won't be checked again, while a target
6414 element with a higher number will also be checked, and potential change
6415 actions will get executed for this CE, too (which is wrong), while
6416 further changes are ignored (which is correct). As this bugfix breaks
6417 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6418 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6419 behaviour for existing levels and tapes that make use of this bug */
6421 level->use_action_after_change_bug = TRUE;
6424 // not centering level after relocating player was default only in 3.2.3
6425 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6426 level->shifted_relocation = TRUE;
6428 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6429 if (level->game_version < VERSION_IDENT(3,2,6,0))
6430 level->em_explodes_by_fire = TRUE;
6432 // levels were solved by the first player entering an exit up to 4.1.0.0
6433 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6434 level->solved_by_one_player = TRUE;
6436 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6437 if (level->game_version < VERSION_IDENT(4,1,1,1))
6438 level->use_life_bugs = TRUE;
6440 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6441 if (level->game_version < VERSION_IDENT(4,1,1,1))
6442 level->sb_objects_needed = FALSE;
6444 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6445 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6446 level->finish_dig_collect = FALSE;
6449 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6453 // map elements that have changed in newer versions
6454 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6455 level->game_version);
6456 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6457 for (x = 0; x < 3; x++)
6458 for (y = 0; y < 3; y++)
6459 level->yamyam_content[i].e[x][y] =
6460 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6461 level->game_version);
6465 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6469 // map custom element change events that have changed in newer versions
6470 // (these following values were accidentally changed in version 3.0.1)
6471 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6472 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6474 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6476 int element = EL_CUSTOM_START + i;
6478 // order of checking and copying events to be mapped is important
6479 // (do not change the start and end value -- they are constant)
6480 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6482 if (HAS_CHANGE_EVENT(element, j - 2))
6484 SET_CHANGE_EVENT(element, j - 2, FALSE);
6485 SET_CHANGE_EVENT(element, j, TRUE);
6489 // order of checking and copying events to be mapped is important
6490 // (do not change the start and end value -- they are constant)
6491 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6493 if (HAS_CHANGE_EVENT(element, j - 1))
6495 SET_CHANGE_EVENT(element, j - 1, FALSE);
6496 SET_CHANGE_EVENT(element, j, TRUE);
6502 // initialize "can_change" field for old levels with only one change page
6503 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6505 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6507 int element = EL_CUSTOM_START + i;
6509 if (CAN_CHANGE(element))
6510 element_info[element].change->can_change = TRUE;
6514 // correct custom element values (for old levels without these options)
6515 if (level->game_version < VERSION_IDENT(3,1,1,0))
6517 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6519 int element = EL_CUSTOM_START + i;
6520 struct ElementInfo *ei = &element_info[element];
6522 if (ei->access_direction == MV_NO_DIRECTION)
6523 ei->access_direction = MV_ALL_DIRECTIONS;
6527 // correct custom element values (fix invalid values for all versions)
6530 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6532 int element = EL_CUSTOM_START + i;
6533 struct ElementInfo *ei = &element_info[element];
6535 for (j = 0; j < ei->num_change_pages; j++)
6537 struct ElementChangeInfo *change = &ei->change_page[j];
6539 if (change->trigger_player == CH_PLAYER_NONE)
6540 change->trigger_player = CH_PLAYER_ANY;
6542 if (change->trigger_side == CH_SIDE_NONE)
6543 change->trigger_side = CH_SIDE_ANY;
6548 // initialize "can_explode" field for old levels which did not store this
6549 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6550 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6552 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6554 int element = EL_CUSTOM_START + i;
6556 if (EXPLODES_1X1_OLD(element))
6557 element_info[element].explosion_type = EXPLODES_1X1;
6559 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6560 EXPLODES_SMASHED(element) ||
6561 EXPLODES_IMPACT(element)));
6565 // correct previously hard-coded move delay values for maze runner style
6566 if (level->game_version < VERSION_IDENT(3,1,1,0))
6568 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6570 int element = EL_CUSTOM_START + i;
6572 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6574 // previously hard-coded and therefore ignored
6575 element_info[element].move_delay_fixed = 9;
6576 element_info[element].move_delay_random = 0;
6581 // set some other uninitialized values of custom elements in older levels
6582 if (level->game_version < VERSION_IDENT(3,1,0,0))
6584 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6586 int element = EL_CUSTOM_START + i;
6588 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6590 element_info[element].explosion_delay = 17;
6591 element_info[element].ignition_delay = 8;
6596 static void LoadLevel_InitElements(struct LevelInfo *level)
6598 LoadLevel_InitStandardElements(level);
6600 if (level->file_has_custom_elements)
6601 LoadLevel_InitCustomElements(level);
6603 // initialize element properties for level editor etc.
6604 InitElementPropertiesEngine(level->game_version);
6605 InitElementPropertiesGfxElement();
6608 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6612 // map elements that have changed in newer versions
6613 for (y = 0; y < level->fieldy; y++)
6614 for (x = 0; x < level->fieldx; x++)
6615 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6616 level->game_version);
6618 // clear unused playfield data (nicer if level gets resized in editor)
6619 for (x = 0; x < MAX_LEV_FIELDX; x++)
6620 for (y = 0; y < MAX_LEV_FIELDY; y++)
6621 if (x >= level->fieldx || y >= level->fieldy)
6622 level->field[x][y] = EL_EMPTY;
6624 // copy elements to runtime playfield array
6625 for (x = 0; x < MAX_LEV_FIELDX; x++)
6626 for (y = 0; y < MAX_LEV_FIELDY; y++)
6627 Tile[x][y] = level->field[x][y];
6629 // initialize level size variables for faster access
6630 lev_fieldx = level->fieldx;
6631 lev_fieldy = level->fieldy;
6633 // determine border element for this level
6634 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6635 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6640 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6642 struct LevelFileInfo *level_file_info = &level->file_info;
6644 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6645 CopyNativeLevel_RND_to_Native(level);
6648 static void LoadLevelTemplate_LoadAndInit(void)
6650 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6652 LoadLevel_InitVersion(&level_template);
6653 LoadLevel_InitElements(&level_template);
6655 ActivateLevelTemplate();
6658 void LoadLevelTemplate(int nr)
6660 if (!fileExists(getGlobalLevelTemplateFilename()))
6662 Warn("no level template found for this level");
6667 setLevelFileInfo(&level_template.file_info, nr);
6669 LoadLevelTemplate_LoadAndInit();
6672 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6674 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6676 LoadLevelTemplate_LoadAndInit();
6679 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6681 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6683 if (level.use_custom_template)
6685 if (network_level != NULL)
6686 LoadNetworkLevelTemplate(network_level);
6688 LoadLevelTemplate(-1);
6691 LoadLevel_InitVersion(&level);
6692 LoadLevel_InitElements(&level);
6693 LoadLevel_InitPlayfield(&level);
6695 LoadLevel_InitNativeEngines(&level);
6698 void LoadLevel(int nr)
6700 SetLevelSetInfo(leveldir_current->identifier, nr);
6702 setLevelFileInfo(&level.file_info, nr);
6704 LoadLevel_LoadAndInit(NULL);
6707 void LoadLevelInfoOnly(int nr)
6709 setLevelFileInfo(&level.file_info, nr);
6711 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6714 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6716 SetLevelSetInfo(network_level->leveldir_identifier,
6717 network_level->file_info.nr);
6719 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6721 LoadLevel_LoadAndInit(network_level);
6724 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6728 chunk_size += putFileVersion(file, level->file_version);
6729 chunk_size += putFileVersion(file, level->game_version);
6734 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6738 chunk_size += putFile16BitBE(file, level->creation_date.year);
6739 chunk_size += putFile8Bit(file, level->creation_date.month);
6740 chunk_size += putFile8Bit(file, level->creation_date.day);
6745 #if ENABLE_HISTORIC_CHUNKS
6746 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6750 putFile8Bit(file, level->fieldx);
6751 putFile8Bit(file, level->fieldy);
6753 putFile16BitBE(file, level->time);
6754 putFile16BitBE(file, level->gems_needed);
6756 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6757 putFile8Bit(file, level->name[i]);
6759 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6760 putFile8Bit(file, level->score[i]);
6762 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6763 for (y = 0; y < 3; y++)
6764 for (x = 0; x < 3; x++)
6765 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6766 level->yamyam_content[i].e[x][y]));
6767 putFile8Bit(file, level->amoeba_speed);
6768 putFile8Bit(file, level->time_magic_wall);
6769 putFile8Bit(file, level->time_wheel);
6770 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6771 level->amoeba_content));
6772 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6773 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6774 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6775 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6777 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6779 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6780 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6781 putFile32BitBE(file, level->can_move_into_acid_bits);
6782 putFile8Bit(file, level->dont_collide_with_bits);
6784 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6785 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6787 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6788 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6789 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6791 putFile8Bit(file, level->game_engine_type);
6793 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6797 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6802 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6803 chunk_size += putFile8Bit(file, level->name[i]);
6808 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6813 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6814 chunk_size += putFile8Bit(file, level->author[i]);
6819 #if ENABLE_HISTORIC_CHUNKS
6820 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6825 for (y = 0; y < level->fieldy; y++)
6826 for (x = 0; x < level->fieldx; x++)
6827 if (level->encoding_16bit_field)
6828 chunk_size += putFile16BitBE(file, level->field[x][y]);
6830 chunk_size += putFile8Bit(file, level->field[x][y]);
6836 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6841 for (y = 0; y < level->fieldy; y++)
6842 for (x = 0; x < level->fieldx; x++)
6843 chunk_size += putFile16BitBE(file, level->field[x][y]);
6848 #if ENABLE_HISTORIC_CHUNKS
6849 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6853 putFile8Bit(file, EL_YAMYAM);
6854 putFile8Bit(file, level->num_yamyam_contents);
6855 putFile8Bit(file, 0);
6856 putFile8Bit(file, 0);
6858 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6859 for (y = 0; y < 3; y++)
6860 for (x = 0; x < 3; x++)
6861 if (level->encoding_16bit_field)
6862 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6864 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6868 #if ENABLE_HISTORIC_CHUNKS
6869 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6872 int num_contents, content_xsize, content_ysize;
6873 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6875 if (element == EL_YAMYAM)
6877 num_contents = level->num_yamyam_contents;
6881 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6882 for (y = 0; y < 3; y++)
6883 for (x = 0; x < 3; x++)
6884 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6886 else if (element == EL_BD_AMOEBA)
6892 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6893 for (y = 0; y < 3; y++)
6894 for (x = 0; x < 3; x++)
6895 content_array[i][x][y] = EL_EMPTY;
6896 content_array[0][0][0] = level->amoeba_content;
6900 // chunk header already written -- write empty chunk data
6901 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6903 Warn("cannot save content for element '%d'", element);
6908 putFile16BitBE(file, element);
6909 putFile8Bit(file, num_contents);
6910 putFile8Bit(file, content_xsize);
6911 putFile8Bit(file, content_ysize);
6913 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6915 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6916 for (y = 0; y < 3; y++)
6917 for (x = 0; x < 3; x++)
6918 putFile16BitBE(file, content_array[i][x][y]);
6922 #if ENABLE_HISTORIC_CHUNKS
6923 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6925 int envelope_nr = element - EL_ENVELOPE_1;
6926 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6930 chunk_size += putFile16BitBE(file, element);
6931 chunk_size += putFile16BitBE(file, envelope_len);
6932 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6933 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6935 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6936 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6938 for (i = 0; i < envelope_len; i++)
6939 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6945 #if ENABLE_HISTORIC_CHUNKS
6946 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6947 int num_changed_custom_elements)
6951 putFile16BitBE(file, num_changed_custom_elements);
6953 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6955 int element = EL_CUSTOM_START + i;
6957 struct ElementInfo *ei = &element_info[element];
6959 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6961 if (check < num_changed_custom_elements)
6963 putFile16BitBE(file, element);
6964 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6971 if (check != num_changed_custom_elements) // should not happen
6972 Warn("inconsistent number of custom element properties");
6976 #if ENABLE_HISTORIC_CHUNKS
6977 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6978 int num_changed_custom_elements)
6982 putFile16BitBE(file, num_changed_custom_elements);
6984 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6986 int element = EL_CUSTOM_START + i;
6988 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6990 if (check < num_changed_custom_elements)
6992 putFile16BitBE(file, element);
6993 putFile16BitBE(file, element_info[element].change->target_element);
7000 if (check != num_changed_custom_elements) // should not happen
7001 Warn("inconsistent number of custom target elements");
7005 #if ENABLE_HISTORIC_CHUNKS
7006 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7007 int num_changed_custom_elements)
7009 int i, j, x, y, check = 0;
7011 putFile16BitBE(file, num_changed_custom_elements);
7013 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7015 int element = EL_CUSTOM_START + i;
7016 struct ElementInfo *ei = &element_info[element];
7018 if (ei->modified_settings)
7020 if (check < num_changed_custom_elements)
7022 putFile16BitBE(file, element);
7024 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7025 putFile8Bit(file, ei->description[j]);
7027 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7029 // some free bytes for future properties and padding
7030 WriteUnusedBytesToFile(file, 7);
7032 putFile8Bit(file, ei->use_gfx_element);
7033 putFile16BitBE(file, ei->gfx_element_initial);
7035 putFile8Bit(file, ei->collect_score_initial);
7036 putFile8Bit(file, ei->collect_count_initial);
7038 putFile16BitBE(file, ei->push_delay_fixed);
7039 putFile16BitBE(file, ei->push_delay_random);
7040 putFile16BitBE(file, ei->move_delay_fixed);
7041 putFile16BitBE(file, ei->move_delay_random);
7043 putFile16BitBE(file, ei->move_pattern);
7044 putFile8Bit(file, ei->move_direction_initial);
7045 putFile8Bit(file, ei->move_stepsize);
7047 for (y = 0; y < 3; y++)
7048 for (x = 0; x < 3; x++)
7049 putFile16BitBE(file, ei->content.e[x][y]);
7051 putFile32BitBE(file, ei->change->events);
7053 putFile16BitBE(file, ei->change->target_element);
7055 putFile16BitBE(file, ei->change->delay_fixed);
7056 putFile16BitBE(file, ei->change->delay_random);
7057 putFile16BitBE(file, ei->change->delay_frames);
7059 putFile16BitBE(file, ei->change->initial_trigger_element);
7061 putFile8Bit(file, ei->change->explode);
7062 putFile8Bit(file, ei->change->use_target_content);
7063 putFile8Bit(file, ei->change->only_if_complete);
7064 putFile8Bit(file, ei->change->use_random_replace);
7066 putFile8Bit(file, ei->change->random_percentage);
7067 putFile8Bit(file, ei->change->replace_when);
7069 for (y = 0; y < 3; y++)
7070 for (x = 0; x < 3; x++)
7071 putFile16BitBE(file, ei->change->content.e[x][y]);
7073 putFile8Bit(file, ei->slippery_type);
7075 // some free bytes for future properties and padding
7076 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7083 if (check != num_changed_custom_elements) // should not happen
7084 Warn("inconsistent number of custom element properties");
7088 #if ENABLE_HISTORIC_CHUNKS
7089 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7091 struct ElementInfo *ei = &element_info[element];
7094 // ---------- custom element base property values (96 bytes) ----------------
7096 putFile16BitBE(file, element);
7098 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7099 putFile8Bit(file, ei->description[i]);
7101 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7103 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7105 putFile8Bit(file, ei->num_change_pages);
7107 putFile16BitBE(file, ei->ce_value_fixed_initial);
7108 putFile16BitBE(file, ei->ce_value_random_initial);
7109 putFile8Bit(file, ei->use_last_ce_value);
7111 putFile8Bit(file, ei->use_gfx_element);
7112 putFile16BitBE(file, ei->gfx_element_initial);
7114 putFile8Bit(file, ei->collect_score_initial);
7115 putFile8Bit(file, ei->collect_count_initial);
7117 putFile8Bit(file, ei->drop_delay_fixed);
7118 putFile8Bit(file, ei->push_delay_fixed);
7119 putFile8Bit(file, ei->drop_delay_random);
7120 putFile8Bit(file, ei->push_delay_random);
7121 putFile16BitBE(file, ei->move_delay_fixed);
7122 putFile16BitBE(file, ei->move_delay_random);
7124 // bits 0 - 15 of "move_pattern" ...
7125 putFile16BitBE(file, ei->move_pattern & 0xffff);
7126 putFile8Bit(file, ei->move_direction_initial);
7127 putFile8Bit(file, ei->move_stepsize);
7129 putFile8Bit(file, ei->slippery_type);
7131 for (y = 0; y < 3; y++)
7132 for (x = 0; x < 3; x++)
7133 putFile16BitBE(file, ei->content.e[x][y]);
7135 putFile16BitBE(file, ei->move_enter_element);
7136 putFile16BitBE(file, ei->move_leave_element);
7137 putFile8Bit(file, ei->move_leave_type);
7139 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7140 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7142 putFile8Bit(file, ei->access_direction);
7144 putFile8Bit(file, ei->explosion_delay);
7145 putFile8Bit(file, ei->ignition_delay);
7146 putFile8Bit(file, ei->explosion_type);
7148 // some free bytes for future custom property values and padding
7149 WriteUnusedBytesToFile(file, 1);
7151 // ---------- change page property values (48 bytes) ------------------------
7153 for (i = 0; i < ei->num_change_pages; i++)
7155 struct ElementChangeInfo *change = &ei->change_page[i];
7156 unsigned int event_bits;
7158 // bits 0 - 31 of "has_event[]" ...
7160 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7161 if (change->has_event[j])
7162 event_bits |= (1 << j);
7163 putFile32BitBE(file, event_bits);
7165 putFile16BitBE(file, change->target_element);
7167 putFile16BitBE(file, change->delay_fixed);
7168 putFile16BitBE(file, change->delay_random);
7169 putFile16BitBE(file, change->delay_frames);
7171 putFile16BitBE(file, change->initial_trigger_element);
7173 putFile8Bit(file, change->explode);
7174 putFile8Bit(file, change->use_target_content);
7175 putFile8Bit(file, change->only_if_complete);
7176 putFile8Bit(file, change->use_random_replace);
7178 putFile8Bit(file, change->random_percentage);
7179 putFile8Bit(file, change->replace_when);
7181 for (y = 0; y < 3; y++)
7182 for (x = 0; x < 3; x++)
7183 putFile16BitBE(file, change->target_content.e[x][y]);
7185 putFile8Bit(file, change->can_change);
7187 putFile8Bit(file, change->trigger_side);
7189 putFile8Bit(file, change->trigger_player);
7190 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7191 log_2(change->trigger_page)));
7193 putFile8Bit(file, change->has_action);
7194 putFile8Bit(file, change->action_type);
7195 putFile8Bit(file, change->action_mode);
7196 putFile16BitBE(file, change->action_arg);
7198 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7200 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7201 if (change->has_event[j])
7202 event_bits |= (1 << (j - 32));
7203 putFile8Bit(file, event_bits);
7208 #if ENABLE_HISTORIC_CHUNKS
7209 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7211 struct ElementInfo *ei = &element_info[element];
7212 struct ElementGroupInfo *group = ei->group;
7215 putFile16BitBE(file, element);
7217 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7218 putFile8Bit(file, ei->description[i]);
7220 putFile8Bit(file, group->num_elements);
7222 putFile8Bit(file, ei->use_gfx_element);
7223 putFile16BitBE(file, ei->gfx_element_initial);
7225 putFile8Bit(file, group->choice_mode);
7227 // some free bytes for future values and padding
7228 WriteUnusedBytesToFile(file, 3);
7230 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7231 putFile16BitBE(file, group->element[i]);
7235 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7236 boolean write_element)
7238 int save_type = entry->save_type;
7239 int data_type = entry->data_type;
7240 int conf_type = entry->conf_type;
7241 int byte_mask = conf_type & CONF_MASK_BYTES;
7242 int element = entry->element;
7243 int default_value = entry->default_value;
7245 boolean modified = FALSE;
7247 if (byte_mask != CONF_MASK_MULTI_BYTES)
7249 void *value_ptr = entry->value;
7250 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7253 // check if any settings have been modified before saving them
7254 if (value != default_value)
7257 // do not save if explicitly told or if unmodified default settings
7258 if ((save_type == SAVE_CONF_NEVER) ||
7259 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7263 num_bytes += putFile16BitBE(file, element);
7265 num_bytes += putFile8Bit(file, conf_type);
7266 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7267 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7268 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7271 else if (data_type == TYPE_STRING)
7273 char *default_string = entry->default_string;
7274 char *string = (char *)(entry->value);
7275 int string_length = strlen(string);
7278 // check if any settings have been modified before saving them
7279 if (!strEqual(string, default_string))
7282 // do not save if explicitly told or if unmodified default settings
7283 if ((save_type == SAVE_CONF_NEVER) ||
7284 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7288 num_bytes += putFile16BitBE(file, element);
7290 num_bytes += putFile8Bit(file, conf_type);
7291 num_bytes += putFile16BitBE(file, string_length);
7293 for (i = 0; i < string_length; i++)
7294 num_bytes += putFile8Bit(file, string[i]);
7296 else if (data_type == TYPE_ELEMENT_LIST)
7298 int *element_array = (int *)(entry->value);
7299 int num_elements = *(int *)(entry->num_entities);
7302 // check if any settings have been modified before saving them
7303 for (i = 0; i < num_elements; i++)
7304 if (element_array[i] != default_value)
7307 // do not save if explicitly told or if unmodified default settings
7308 if ((save_type == SAVE_CONF_NEVER) ||
7309 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7313 num_bytes += putFile16BitBE(file, element);
7315 num_bytes += putFile8Bit(file, conf_type);
7316 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7318 for (i = 0; i < num_elements; i++)
7319 num_bytes += putFile16BitBE(file, element_array[i]);
7321 else if (data_type == TYPE_CONTENT_LIST)
7323 struct Content *content = (struct Content *)(entry->value);
7324 int num_contents = *(int *)(entry->num_entities);
7327 // check if any settings have been modified before saving them
7328 for (i = 0; i < num_contents; i++)
7329 for (y = 0; y < 3; y++)
7330 for (x = 0; x < 3; x++)
7331 if (content[i].e[x][y] != default_value)
7334 // do not save if explicitly told or if unmodified default settings
7335 if ((save_type == SAVE_CONF_NEVER) ||
7336 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7340 num_bytes += putFile16BitBE(file, element);
7342 num_bytes += putFile8Bit(file, conf_type);
7343 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7345 for (i = 0; i < num_contents; i++)
7346 for (y = 0; y < 3; y++)
7347 for (x = 0; x < 3; x++)
7348 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7354 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7359 li = *level; // copy level data into temporary buffer
7361 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7362 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7367 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7372 li = *level; // copy level data into temporary buffer
7374 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7375 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7380 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7382 int envelope_nr = element - EL_ENVELOPE_1;
7386 chunk_size += putFile16BitBE(file, element);
7388 // copy envelope data into temporary buffer
7389 xx_envelope = level->envelope[envelope_nr];
7391 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7392 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7397 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7399 struct ElementInfo *ei = &element_info[element];
7403 chunk_size += putFile16BitBE(file, element);
7405 xx_ei = *ei; // copy element data into temporary buffer
7407 // set default description string for this specific element
7408 strcpy(xx_default_description, getDefaultElementDescription(ei));
7410 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7411 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7413 for (i = 0; i < ei->num_change_pages; i++)
7415 struct ElementChangeInfo *change = &ei->change_page[i];
7417 xx_current_change_page = i;
7419 xx_change = *change; // copy change data into temporary buffer
7422 setEventBitsFromEventFlags(change);
7424 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7425 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7432 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7434 struct ElementInfo *ei = &element_info[element];
7435 struct ElementGroupInfo *group = ei->group;
7439 chunk_size += putFile16BitBE(file, element);
7441 xx_ei = *ei; // copy element data into temporary buffer
7442 xx_group = *group; // copy group data into temporary buffer
7444 // set default description string for this specific element
7445 strcpy(xx_default_description, getDefaultElementDescription(ei));
7447 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7448 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7453 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7454 boolean save_as_template)
7460 if (!(file = fopen(filename, MODE_WRITE)))
7462 Warn("cannot save level file '%s'", filename);
7467 level->file_version = FILE_VERSION_ACTUAL;
7468 level->game_version = GAME_VERSION_ACTUAL;
7470 level->creation_date = getCurrentDate();
7472 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7473 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7475 chunk_size = SaveLevel_VERS(NULL, level);
7476 putFileChunkBE(file, "VERS", chunk_size);
7477 SaveLevel_VERS(file, level);
7479 chunk_size = SaveLevel_DATE(NULL, level);
7480 putFileChunkBE(file, "DATE", chunk_size);
7481 SaveLevel_DATE(file, level);
7483 chunk_size = SaveLevel_NAME(NULL, level);
7484 putFileChunkBE(file, "NAME", chunk_size);
7485 SaveLevel_NAME(file, level);
7487 chunk_size = SaveLevel_AUTH(NULL, level);
7488 putFileChunkBE(file, "AUTH", chunk_size);
7489 SaveLevel_AUTH(file, level);
7491 chunk_size = SaveLevel_INFO(NULL, level);
7492 putFileChunkBE(file, "INFO", chunk_size);
7493 SaveLevel_INFO(file, level);
7495 chunk_size = SaveLevel_BODY(NULL, level);
7496 putFileChunkBE(file, "BODY", chunk_size);
7497 SaveLevel_BODY(file, level);
7499 chunk_size = SaveLevel_ELEM(NULL, level);
7500 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7502 putFileChunkBE(file, "ELEM", chunk_size);
7503 SaveLevel_ELEM(file, level);
7506 for (i = 0; i < NUM_ENVELOPES; i++)
7508 int element = EL_ENVELOPE_1 + i;
7510 chunk_size = SaveLevel_NOTE(NULL, level, element);
7511 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7513 putFileChunkBE(file, "NOTE", chunk_size);
7514 SaveLevel_NOTE(file, level, element);
7518 // if not using template level, check for non-default custom/group elements
7519 if (!level->use_custom_template || save_as_template)
7521 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7523 int element = EL_CUSTOM_START + i;
7525 chunk_size = SaveLevel_CUSX(NULL, level, element);
7526 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7528 putFileChunkBE(file, "CUSX", chunk_size);
7529 SaveLevel_CUSX(file, level, element);
7533 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7535 int element = EL_GROUP_START + i;
7537 chunk_size = SaveLevel_GRPX(NULL, level, element);
7538 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7540 putFileChunkBE(file, "GRPX", chunk_size);
7541 SaveLevel_GRPX(file, level, element);
7548 SetFilePermissions(filename, PERMS_PRIVATE);
7551 void SaveLevel(int nr)
7553 char *filename = getDefaultLevelFilename(nr);
7555 SaveLevelFromFilename(&level, filename, FALSE);
7558 void SaveLevelTemplate(void)
7560 char *filename = getLocalLevelTemplateFilename();
7562 SaveLevelFromFilename(&level, filename, TRUE);
7565 boolean SaveLevelChecked(int nr)
7567 char *filename = getDefaultLevelFilename(nr);
7568 boolean new_level = !fileExists(filename);
7569 boolean level_saved = FALSE;
7571 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7576 Request("Level saved!", REQ_CONFIRM);
7584 void DumpLevel(struct LevelInfo *level)
7586 if (level->no_level_file || level->no_valid_file)
7588 Warn("cannot dump -- no valid level file found");
7594 Print("Level xxx (file version %08d, game version %08d)\n",
7595 level->file_version, level->game_version);
7598 Print("Level author: '%s'\n", level->author);
7599 Print("Level title: '%s'\n", level->name);
7601 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7603 Print("Level time: %d seconds\n", level->time);
7604 Print("Gems needed: %d\n", level->gems_needed);
7606 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7607 Print("Time for wheel: %d seconds\n", level->time_wheel);
7608 Print("Time for light: %d seconds\n", level->time_light);
7609 Print("Time for timegate: %d seconds\n", level->time_timegate);
7611 Print("Amoeba speed: %d\n", level->amoeba_speed);
7614 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7615 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7616 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7617 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7618 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7624 // ============================================================================
7625 // tape file functions
7626 // ============================================================================
7628 static void setTapeInfoToDefaults(void)
7632 // always start with reliable default values (empty tape)
7635 // default values (also for pre-1.2 tapes) with only the first player
7636 tape.player_participates[0] = TRUE;
7637 for (i = 1; i < MAX_PLAYERS; i++)
7638 tape.player_participates[i] = FALSE;
7640 // at least one (default: the first) player participates in every tape
7641 tape.num_participating_players = 1;
7643 tape.property_bits = TAPE_PROPERTY_NONE;
7645 tape.level_nr = level_nr;
7647 tape.changed = FALSE;
7649 tape.recording = FALSE;
7650 tape.playing = FALSE;
7651 tape.pausing = FALSE;
7653 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7654 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7656 tape.no_valid_file = FALSE;
7659 static int getTapePosSize(struct TapeInfo *tape)
7661 int tape_pos_size = 0;
7663 if (tape->use_key_actions)
7664 tape_pos_size += tape->num_participating_players;
7666 if (tape->use_mouse_actions)
7667 tape_pos_size += 3; // x and y position and mouse button mask
7669 tape_pos_size += 1; // tape action delay value
7671 return tape_pos_size;
7674 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7676 tape->use_key_actions = FALSE;
7677 tape->use_mouse_actions = FALSE;
7679 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7680 tape->use_key_actions = TRUE;
7682 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7683 tape->use_mouse_actions = TRUE;
7686 static int getTapeActionValue(struct TapeInfo *tape)
7688 return (tape->use_key_actions &&
7689 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7690 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7691 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7692 TAPE_ACTIONS_DEFAULT);
7695 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7697 tape->file_version = getFileVersion(file);
7698 tape->game_version = getFileVersion(file);
7703 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7707 tape->random_seed = getFile32BitBE(file);
7708 tape->date = getFile32BitBE(file);
7709 tape->length = getFile32BitBE(file);
7711 // read header fields that are new since version 1.2
7712 if (tape->file_version >= FILE_VERSION_1_2)
7714 byte store_participating_players = getFile8Bit(file);
7717 // since version 1.2, tapes store which players participate in the tape
7718 tape->num_participating_players = 0;
7719 for (i = 0; i < MAX_PLAYERS; i++)
7721 tape->player_participates[i] = FALSE;
7723 if (store_participating_players & (1 << i))
7725 tape->player_participates[i] = TRUE;
7726 tape->num_participating_players++;
7730 setTapeActionFlags(tape, getFile8Bit(file));
7732 tape->property_bits = getFile8Bit(file);
7734 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7736 engine_version = getFileVersion(file);
7737 if (engine_version > 0)
7738 tape->engine_version = engine_version;
7740 tape->engine_version = tape->game_version;
7746 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7748 tape->scr_fieldx = getFile8Bit(file);
7749 tape->scr_fieldy = getFile8Bit(file);
7754 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7756 int level_identifier_size;
7759 level_identifier_size = getFile16BitBE(file);
7761 tape->level_identifier =
7762 checked_realloc(tape->level_identifier, level_identifier_size);
7764 for (i = 0; i < level_identifier_size; i++)
7765 tape->level_identifier[i] = getFile8Bit(file);
7767 tape->level_nr = getFile16BitBE(file);
7769 chunk_size = 2 + level_identifier_size + 2;
7774 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7777 int tape_pos_size = getTapePosSize(tape);
7778 int chunk_size_expected = tape_pos_size * tape->length;
7780 if (chunk_size_expected != chunk_size)
7782 ReadUnusedBytesFromFile(file, chunk_size);
7783 return chunk_size_expected;
7786 for (i = 0; i < tape->length; i++)
7788 if (i >= MAX_TAPE_LEN)
7790 Warn("tape truncated -- size exceeds maximum tape size %d",
7793 // tape too large; read and ignore remaining tape data from this chunk
7794 for (;i < tape->length; i++)
7795 ReadUnusedBytesFromFile(file, tape_pos_size);
7800 if (tape->use_key_actions)
7802 for (j = 0; j < MAX_PLAYERS; j++)
7804 tape->pos[i].action[j] = MV_NONE;
7806 if (tape->player_participates[j])
7807 tape->pos[i].action[j] = getFile8Bit(file);
7811 if (tape->use_mouse_actions)
7813 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7814 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7815 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7818 tape->pos[i].delay = getFile8Bit(file);
7820 if (tape->file_version == FILE_VERSION_1_0)
7822 // eliminate possible diagonal moves in old tapes
7823 // this is only for backward compatibility
7825 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7826 byte action = tape->pos[i].action[0];
7827 int k, num_moves = 0;
7829 for (k = 0; k<4; k++)
7831 if (action & joy_dir[k])
7833 tape->pos[i + num_moves].action[0] = joy_dir[k];
7835 tape->pos[i + num_moves].delay = 0;
7844 tape->length += num_moves;
7847 else if (tape->file_version < FILE_VERSION_2_0)
7849 // convert pre-2.0 tapes to new tape format
7851 if (tape->pos[i].delay > 1)
7854 tape->pos[i + 1] = tape->pos[i];
7855 tape->pos[i + 1].delay = 1;
7858 for (j = 0; j < MAX_PLAYERS; j++)
7859 tape->pos[i].action[j] = MV_NONE;
7860 tape->pos[i].delay--;
7867 if (checkEndOfFile(file))
7871 if (i != tape->length)
7872 chunk_size = tape_pos_size * i;
7877 static void LoadTape_SokobanSolution(char *filename)
7880 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7882 if (!(file = openFile(filename, MODE_READ)))
7884 tape.no_valid_file = TRUE;
7889 while (!checkEndOfFile(file))
7891 unsigned char c = getByteFromFile(file);
7893 if (checkEndOfFile(file))
7900 tape.pos[tape.length].action[0] = MV_UP;
7901 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7907 tape.pos[tape.length].action[0] = MV_DOWN;
7908 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7914 tape.pos[tape.length].action[0] = MV_LEFT;
7915 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7921 tape.pos[tape.length].action[0] = MV_RIGHT;
7922 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7930 // ignore white-space characters
7934 tape.no_valid_file = TRUE;
7936 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
7944 if (tape.no_valid_file)
7947 tape.length_frames = GetTapeLengthFrames();
7948 tape.length_seconds = GetTapeLengthSeconds();
7951 void LoadTapeFromFilename(char *filename)
7953 char cookie[MAX_LINE_LEN];
7954 char chunk_name[CHUNK_ID_LEN + 1];
7958 // always start with reliable default values
7959 setTapeInfoToDefaults();
7961 if (strSuffix(filename, ".sln"))
7963 LoadTape_SokobanSolution(filename);
7968 if (!(file = openFile(filename, MODE_READ)))
7970 tape.no_valid_file = TRUE;
7975 getFileChunkBE(file, chunk_name, NULL);
7976 if (strEqual(chunk_name, "RND1"))
7978 getFile32BitBE(file); // not used
7980 getFileChunkBE(file, chunk_name, NULL);
7981 if (!strEqual(chunk_name, "TAPE"))
7983 tape.no_valid_file = TRUE;
7985 Warn("unknown format of tape file '%s'", filename);
7992 else // check for pre-2.0 file format with cookie string
7994 strcpy(cookie, chunk_name);
7995 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7997 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7998 cookie[strlen(cookie) - 1] = '\0';
8000 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8002 tape.no_valid_file = TRUE;
8004 Warn("unknown format of tape file '%s'", filename);
8011 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8013 tape.no_valid_file = TRUE;
8015 Warn("unsupported version of tape file '%s'", filename);
8022 // pre-2.0 tape files have no game version, so use file version here
8023 tape.game_version = tape.file_version;
8026 if (tape.file_version < FILE_VERSION_1_2)
8028 // tape files from versions before 1.2.0 without chunk structure
8029 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8030 LoadTape_BODY(file, 2 * tape.length, &tape);
8038 int (*loader)(File *, int, struct TapeInfo *);
8042 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8043 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8044 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8045 { "INFO", -1, LoadTape_INFO },
8046 { "BODY", -1, LoadTape_BODY },
8050 while (getFileChunkBE(file, chunk_name, &chunk_size))
8054 while (chunk_info[i].name != NULL &&
8055 !strEqual(chunk_name, chunk_info[i].name))
8058 if (chunk_info[i].name == NULL)
8060 Warn("unknown chunk '%s' in tape file '%s'",
8061 chunk_name, filename);
8063 ReadUnusedBytesFromFile(file, chunk_size);
8065 else if (chunk_info[i].size != -1 &&
8066 chunk_info[i].size != chunk_size)
8068 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8069 chunk_size, chunk_name, filename);
8071 ReadUnusedBytesFromFile(file, chunk_size);
8075 // call function to load this tape chunk
8076 int chunk_size_expected =
8077 (chunk_info[i].loader)(file, chunk_size, &tape);
8079 // the size of some chunks cannot be checked before reading other
8080 // chunks first (like "HEAD" and "BODY") that contain some header
8081 // information, so check them here
8082 if (chunk_size_expected != chunk_size)
8084 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8085 chunk_size, chunk_name, filename);
8093 tape.length_frames = GetTapeLengthFrames();
8094 tape.length_seconds = GetTapeLengthSeconds();
8097 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8099 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8101 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8102 tape.engine_version);
8106 void LoadTape(int nr)
8108 char *filename = getTapeFilename(nr);
8110 LoadTapeFromFilename(filename);
8113 void LoadSolutionTape(int nr)
8115 char *filename = getSolutionTapeFilename(nr);
8117 LoadTapeFromFilename(filename);
8119 if (TAPE_IS_EMPTY(tape) &&
8120 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8121 level.native_sp_level->demo.is_available)
8122 CopyNativeTape_SP_to_RND(&level);
8125 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8127 // chunk required for team mode tapes with non-default screen size
8128 return (tape->num_participating_players > 1 &&
8129 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8130 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8133 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8135 putFileVersion(file, tape->file_version);
8136 putFileVersion(file, tape->game_version);
8139 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8142 byte store_participating_players = 0;
8144 // set bits for participating players for compact storage
8145 for (i = 0; i < MAX_PLAYERS; i++)
8146 if (tape->player_participates[i])
8147 store_participating_players |= (1 << i);
8149 putFile32BitBE(file, tape->random_seed);
8150 putFile32BitBE(file, tape->date);
8151 putFile32BitBE(file, tape->length);
8153 putFile8Bit(file, store_participating_players);
8155 putFile8Bit(file, getTapeActionValue(tape));
8157 putFile8Bit(file, tape->property_bits);
8159 // unused bytes not at the end here for 4-byte alignment of engine_version
8160 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8162 putFileVersion(file, tape->engine_version);
8165 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8167 putFile8Bit(file, tape->scr_fieldx);
8168 putFile8Bit(file, tape->scr_fieldy);
8171 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8173 int level_identifier_size = strlen(tape->level_identifier) + 1;
8176 putFile16BitBE(file, level_identifier_size);
8178 for (i = 0; i < level_identifier_size; i++)
8179 putFile8Bit(file, tape->level_identifier[i]);
8181 putFile16BitBE(file, tape->level_nr);
8184 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8188 for (i = 0; i < tape->length; i++)
8190 if (tape->use_key_actions)
8192 for (j = 0; j < MAX_PLAYERS; j++)
8193 if (tape->player_participates[j])
8194 putFile8Bit(file, tape->pos[i].action[j]);
8197 if (tape->use_mouse_actions)
8199 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8200 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8201 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8204 putFile8Bit(file, tape->pos[i].delay);
8208 void SaveTapeToFilename(char *filename)
8212 int info_chunk_size;
8213 int body_chunk_size;
8215 if (!(file = fopen(filename, MODE_WRITE)))
8217 Warn("cannot save level recording file '%s'", filename);
8222 tape_pos_size = getTapePosSize(&tape);
8224 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8225 body_chunk_size = tape_pos_size * tape.length;
8227 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8228 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8230 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8231 SaveTape_VERS(file, &tape);
8233 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8234 SaveTape_HEAD(file, &tape);
8236 if (checkSaveTape_SCRN(&tape))
8238 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8239 SaveTape_SCRN(file, &tape);
8242 putFileChunkBE(file, "INFO", info_chunk_size);
8243 SaveTape_INFO(file, &tape);
8245 putFileChunkBE(file, "BODY", body_chunk_size);
8246 SaveTape_BODY(file, &tape);
8250 SetFilePermissions(filename, PERMS_PRIVATE);
8253 void SaveTape(int nr)
8255 char *filename = getTapeFilename(nr);
8258 InitTapeDirectory(leveldir_current->subdir);
8260 tape.file_version = FILE_VERSION_ACTUAL;
8261 tape.game_version = GAME_VERSION_ACTUAL;
8263 tape.num_participating_players = 0;
8265 // count number of participating players
8266 for (i = 0; i < MAX_PLAYERS; i++)
8267 if (tape.player_participates[i])
8268 tape.num_participating_players++;
8270 SaveTapeToFilename(filename);
8272 tape.changed = FALSE;
8275 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8276 unsigned int req_state_added)
8278 char *filename = getTapeFilename(nr);
8279 boolean new_tape = !fileExists(filename);
8280 boolean tape_saved = FALSE;
8282 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8287 Request(msg_saved, REQ_CONFIRM | req_state_added);
8295 boolean SaveTapeChecked(int nr)
8297 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8300 boolean SaveTapeChecked_LevelSolved(int nr)
8302 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8303 "Level solved! Tape saved!", REQ_STAY_OPEN);
8306 void DumpTape(struct TapeInfo *tape)
8308 int tape_frame_counter;
8311 if (tape->no_valid_file)
8313 Warn("cannot dump -- no valid tape file found");
8319 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8320 tape->level_nr, tape->file_version, tape->game_version);
8321 Print(" (effective engine version %08d)\n",
8322 tape->engine_version);
8323 Print("Level series identifier: '%s'\n", tape->level_identifier);
8326 tape_frame_counter = 0;
8328 for (i = 0; i < tape->length; i++)
8330 if (i >= MAX_TAPE_LEN)
8335 for (j = 0; j < MAX_PLAYERS; j++)
8337 if (tape->player_participates[j])
8339 int action = tape->pos[i].action[j];
8341 Print("%d:%02x ", j, action);
8342 Print("[%c%c%c%c|%c%c] - ",
8343 (action & JOY_LEFT ? '<' : ' '),
8344 (action & JOY_RIGHT ? '>' : ' '),
8345 (action & JOY_UP ? '^' : ' '),
8346 (action & JOY_DOWN ? 'v' : ' '),
8347 (action & JOY_BUTTON_1 ? '1' : ' '),
8348 (action & JOY_BUTTON_2 ? '2' : ' '));
8352 Print("(%03d) ", tape->pos[i].delay);
8353 Print("[%05d]\n", tape_frame_counter);
8355 tape_frame_counter += tape->pos[i].delay;
8362 // ============================================================================
8363 // score file functions
8364 // ============================================================================
8366 void LoadScore(int nr)
8369 char *filename = getScoreFilename(nr);
8370 char cookie[MAX_LINE_LEN];
8371 char line[MAX_LINE_LEN];
8375 // always start with reliable default values
8376 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8378 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8379 highscore[i].Score = 0;
8382 if (!(file = fopen(filename, MODE_READ)))
8385 // check file identifier
8386 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8388 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8389 cookie[strlen(cookie) - 1] = '\0';
8391 if (!checkCookieString(cookie, SCORE_COOKIE))
8393 Warn("unknown format of score file '%s'", filename);
8400 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8402 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8403 Warn("fscanf() failed; %s", strerror(errno));
8405 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8408 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8409 line[strlen(line) - 1] = '\0';
8411 for (line_ptr = line; *line_ptr; line_ptr++)
8413 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8415 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8416 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8425 void SaveScore(int nr)
8428 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8429 char *filename = getScoreFilename(nr);
8432 // used instead of "leveldir_current->subdir" (for network games)
8433 InitScoreDirectory(levelset.identifier);
8435 if (!(file = fopen(filename, MODE_WRITE)))
8437 Warn("cannot save score for level %d", nr);
8442 fprintf(file, "%s\n\n", SCORE_COOKIE);
8444 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8445 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8449 SetFilePermissions(filename, permissions);
8453 // ============================================================================
8454 // setup file functions
8455 // ============================================================================
8457 #define TOKEN_STR_PLAYER_PREFIX "player_"
8460 static struct TokenInfo global_setup_tokens[] =
8464 &setup.player_name, "player_name"
8468 &setup.multiple_users, "multiple_users"
8472 &setup.sound, "sound"
8476 &setup.sound_loops, "repeating_sound_loops"
8480 &setup.sound_music, "background_music"
8484 &setup.sound_simple, "simple_sound_effects"
8488 &setup.toons, "toons"
8492 &setup.scroll_delay, "scroll_delay"
8496 &setup.forced_scroll_delay, "forced_scroll_delay"
8500 &setup.scroll_delay_value, "scroll_delay_value"
8504 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8508 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8512 &setup.fade_screens, "fade_screens"
8516 &setup.autorecord, "automatic_tape_recording"
8520 &setup.show_titlescreen, "show_titlescreen"
8524 &setup.quick_doors, "quick_doors"
8528 &setup.team_mode, "team_mode"
8532 &setup.handicap, "handicap"
8536 &setup.skip_levels, "skip_levels"
8540 &setup.increment_levels, "increment_levels"
8544 &setup.auto_play_next_level, "auto_play_next_level"
8548 &setup.count_score_after_game, "count_score_after_game"
8552 &setup.show_scores_after_game, "show_scores_after_game"
8556 &setup.time_limit, "time_limit"
8560 &setup.fullscreen, "fullscreen"
8564 &setup.window_scaling_percent, "window_scaling_percent"
8568 &setup.window_scaling_quality, "window_scaling_quality"
8572 &setup.screen_rendering_mode, "screen_rendering_mode"
8576 &setup.vsync_mode, "vsync_mode"
8580 &setup.ask_on_escape, "ask_on_escape"
8584 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8588 &setup.ask_on_game_over, "ask_on_game_over"
8592 &setup.quick_switch, "quick_player_switch"
8596 &setup.input_on_focus, "input_on_focus"
8600 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8604 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
8608 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
8612 &setup.game_speed_extended, "game_speed_extended"
8616 &setup.game_frame_delay, "game_frame_delay"
8620 &setup.sp_show_border_elements, "sp_show_border_elements"
8624 &setup.small_game_graphics, "small_game_graphics"
8628 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8632 &setup.graphics_set, "graphics_set"
8636 &setup.sounds_set, "sounds_set"
8640 &setup.music_set, "music_set"
8644 &setup.override_level_graphics, "override_level_graphics"
8648 &setup.override_level_sounds, "override_level_sounds"
8652 &setup.override_level_music, "override_level_music"
8656 &setup.volume_simple, "volume_simple"
8660 &setup.volume_loops, "volume_loops"
8664 &setup.volume_music, "volume_music"
8668 &setup.network_mode, "network_mode"
8672 &setup.network_player_nr, "network_player"
8676 &setup.network_server_hostname, "network_server_hostname"
8680 &setup.touch.control_type, "touch.control_type"
8684 &setup.touch.move_distance, "touch.move_distance"
8688 &setup.touch.drop_distance, "touch.drop_distance"
8692 &setup.touch.transparency, "touch.transparency"
8696 &setup.touch.draw_outlined, "touch.draw_outlined"
8700 &setup.touch.draw_pressed, "touch.draw_pressed"
8704 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
8708 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
8712 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
8716 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
8720 static struct TokenInfo auto_setup_tokens[] =
8724 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
8728 static struct TokenInfo editor_setup_tokens[] =
8732 &setup.editor.el_classic, "editor.el_classic"
8736 &setup.editor.el_custom, "editor.el_custom"
8740 &setup.editor.el_user_defined, "editor.el_user_defined"
8744 &setup.editor.el_dynamic, "editor.el_dynamic"
8748 &setup.editor.el_headlines, "editor.el_headlines"
8752 &setup.editor.show_element_token, "editor.show_element_token"
8756 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
8760 static struct TokenInfo editor_cascade_setup_tokens[] =
8764 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
8768 &setup.editor_cascade.el_em, "editor.cascade.el_em"
8772 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
8776 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
8780 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
8784 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
8788 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
8792 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
8796 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
8800 &setup.editor_cascade.el_df, "editor.cascade.el_df"
8804 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
8808 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
8812 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
8816 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
8820 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
8824 &setup.editor_cascade.el_user, "editor.cascade.el_user"
8828 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
8832 static struct TokenInfo shortcut_setup_tokens[] =
8836 &setup.shortcut.save_game, "shortcut.save_game"
8840 &setup.shortcut.load_game, "shortcut.load_game"
8844 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
8848 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
8852 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
8856 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
8860 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
8864 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
8868 &setup.shortcut.tape_eject, "shortcut.tape_eject"
8872 &setup.shortcut.tape_extra, "shortcut.tape_extra"
8876 &setup.shortcut.tape_stop, "shortcut.tape_stop"
8880 &setup.shortcut.tape_pause, "shortcut.tape_pause"
8884 &setup.shortcut.tape_record, "shortcut.tape_record"
8888 &setup.shortcut.tape_play, "shortcut.tape_play"
8892 &setup.shortcut.sound_simple, "shortcut.sound_simple"
8896 &setup.shortcut.sound_loops, "shortcut.sound_loops"
8900 &setup.shortcut.sound_music, "shortcut.sound_music"
8904 &setup.shortcut.snap_left, "shortcut.snap_left"
8908 &setup.shortcut.snap_right, "shortcut.snap_right"
8912 &setup.shortcut.snap_up, "shortcut.snap_up"
8916 &setup.shortcut.snap_down, "shortcut.snap_down"
8920 static struct SetupInputInfo setup_input;
8921 static struct TokenInfo player_setup_tokens[] =
8925 &setup_input.use_joystick, ".use_joystick"
8929 &setup_input.joy.device_name, ".joy.device_name"
8933 &setup_input.joy.xleft, ".joy.xleft"
8937 &setup_input.joy.xmiddle, ".joy.xmiddle"
8941 &setup_input.joy.xright, ".joy.xright"
8945 &setup_input.joy.yupper, ".joy.yupper"
8949 &setup_input.joy.ymiddle, ".joy.ymiddle"
8953 &setup_input.joy.ylower, ".joy.ylower"
8957 &setup_input.joy.snap, ".joy.snap_field"
8961 &setup_input.joy.drop, ".joy.place_bomb"
8965 &setup_input.key.left, ".key.move_left"
8969 &setup_input.key.right, ".key.move_right"
8973 &setup_input.key.up, ".key.move_up"
8977 &setup_input.key.down, ".key.move_down"
8981 &setup_input.key.snap, ".key.snap_field"
8985 &setup_input.key.drop, ".key.place_bomb"
8989 static struct TokenInfo system_setup_tokens[] =
8993 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
8997 &setup.system.sdl_videodriver, "system.sdl_videodriver"
9001 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
9005 &setup.system.audio_fragment_size, "system.audio_fragment_size"
9009 static struct TokenInfo internal_setup_tokens[] =
9013 &setup.internal.program_title, "program_title"
9017 &setup.internal.program_version, "program_version"
9021 &setup.internal.program_author, "program_author"
9025 &setup.internal.program_email, "program_email"
9029 &setup.internal.program_website, "program_website"
9033 &setup.internal.program_copyright, "program_copyright"
9037 &setup.internal.program_company, "program_company"
9041 &setup.internal.program_icon_file, "program_icon_file"
9045 &setup.internal.default_graphics_set, "default_graphics_set"
9049 &setup.internal.default_sounds_set, "default_sounds_set"
9053 &setup.internal.default_music_set, "default_music_set"
9057 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
9061 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
9065 &setup.internal.fallback_music_file, "fallback_music_file"
9069 &setup.internal.default_level_series, "default_level_series"
9073 &setup.internal.default_window_width, "default_window_width"
9077 &setup.internal.default_window_height, "default_window_height"
9081 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
9085 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
9089 &setup.internal.create_user_levelset, "create_user_levelset"
9093 &setup.internal.menu_game, "menu_game"
9097 &setup.internal.menu_editor, "menu_editor"
9101 &setup.internal.menu_graphics, "menu_graphics"
9105 &setup.internal.menu_sound, "menu_sound"
9109 &setup.internal.menu_artwork, "menu_artwork"
9113 &setup.internal.menu_input, "menu_input"
9117 &setup.internal.menu_touch, "menu_touch"
9121 &setup.internal.menu_shortcuts, "menu_shortcuts"
9125 &setup.internal.menu_exit, "menu_exit"
9129 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9133 static struct TokenInfo debug_setup_tokens[] =
9137 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9141 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9145 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9149 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9153 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9157 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9161 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9165 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9169 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9173 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9177 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9181 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9185 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9189 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9193 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9197 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9201 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9205 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9209 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9213 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9217 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9220 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9224 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9228 &setup.debug.xsn_mode, "debug.xsn_mode"
9232 &setup.debug.xsn_percent, "debug.xsn_percent"
9236 static struct TokenInfo options_setup_tokens[] =
9240 &setup.options.verbose, "options.verbose"
9244 static void setSetupInfoToDefaults(struct SetupInfo *si)
9248 si->player_name = getStringCopy(getDefaultUserName(user.nr));
9250 si->multiple_users = TRUE;
9253 si->sound_loops = TRUE;
9254 si->sound_music = TRUE;
9255 si->sound_simple = TRUE;
9257 si->scroll_delay = TRUE;
9258 si->forced_scroll_delay = FALSE;
9259 si->scroll_delay_value = STD_SCROLL_DELAY;
9260 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9261 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9262 si->fade_screens = TRUE;
9263 si->autorecord = TRUE;
9264 si->show_titlescreen = TRUE;
9265 si->quick_doors = FALSE;
9266 si->team_mode = FALSE;
9267 si->handicap = TRUE;
9268 si->skip_levels = TRUE;
9269 si->increment_levels = TRUE;
9270 si->auto_play_next_level = TRUE;
9271 si->count_score_after_game = TRUE;
9272 si->show_scores_after_game = TRUE;
9273 si->time_limit = TRUE;
9274 si->fullscreen = FALSE;
9275 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9276 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9277 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9278 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9279 si->ask_on_escape = TRUE;
9280 si->ask_on_escape_editor = TRUE;
9281 si->ask_on_game_over = TRUE;
9282 si->quick_switch = FALSE;
9283 si->input_on_focus = FALSE;
9284 si->prefer_aga_graphics = TRUE;
9285 si->prefer_lowpass_sounds = FALSE;
9286 si->prefer_extra_panel_items = TRUE;
9287 si->game_speed_extended = FALSE;
9288 si->game_frame_delay = GAME_FRAME_DELAY;
9289 si->sp_show_border_elements = FALSE;
9290 si->small_game_graphics = FALSE;
9291 si->show_snapshot_buttons = FALSE;
9293 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9294 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9295 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9297 si->override_level_graphics = FALSE;
9298 si->override_level_sounds = FALSE;
9299 si->override_level_music = FALSE;
9301 si->volume_simple = 100; // percent
9302 si->volume_loops = 100; // percent
9303 si->volume_music = 100; // percent
9305 si->network_mode = FALSE;
9306 si->network_player_nr = 0; // first player
9307 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9309 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9310 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9311 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9312 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9313 si->touch.draw_outlined = TRUE;
9314 si->touch.draw_pressed = TRUE;
9316 for (i = 0; i < 2; i++)
9318 char *default_grid_button[6][2] =
9324 { "111222", " vv " },
9325 { "111222", " vv " }
9327 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9328 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9329 int min_xsize = MIN(6, grid_xsize);
9330 int min_ysize = MIN(6, grid_ysize);
9331 int startx = grid_xsize - min_xsize;
9332 int starty = grid_ysize - min_ysize;
9335 // virtual buttons grid can only be set to defaults if video is initialized
9336 // (this will be repeated if virtual buttons are not loaded from setup file)
9337 if (video.initialized)
9339 si->touch.grid_xsize[i] = grid_xsize;
9340 si->touch.grid_ysize[i] = grid_ysize;
9344 si->touch.grid_xsize[i] = -1;
9345 si->touch.grid_ysize[i] = -1;
9348 for (x = 0; x < MAX_GRID_XSIZE; x++)
9349 for (y = 0; y < MAX_GRID_YSIZE; y++)
9350 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9352 for (x = 0; x < min_xsize; x++)
9353 for (y = 0; y < min_ysize; y++)
9354 si->touch.grid_button[i][x][starty + y] =
9355 default_grid_button[y][0][x];
9357 for (x = 0; x < min_xsize; x++)
9358 for (y = 0; y < min_ysize; y++)
9359 si->touch.grid_button[i][startx + x][starty + y] =
9360 default_grid_button[y][1][x];
9363 si->touch.grid_initialized = video.initialized;
9365 si->editor.el_boulderdash = TRUE;
9366 si->editor.el_emerald_mine = TRUE;
9367 si->editor.el_emerald_mine_club = TRUE;
9368 si->editor.el_more = TRUE;
9369 si->editor.el_sokoban = TRUE;
9370 si->editor.el_supaplex = TRUE;
9371 si->editor.el_diamond_caves = TRUE;
9372 si->editor.el_dx_boulderdash = TRUE;
9374 si->editor.el_mirror_magic = TRUE;
9375 si->editor.el_deflektor = TRUE;
9377 si->editor.el_chars = TRUE;
9378 si->editor.el_steel_chars = TRUE;
9380 si->editor.el_classic = TRUE;
9381 si->editor.el_custom = TRUE;
9383 si->editor.el_user_defined = FALSE;
9384 si->editor.el_dynamic = TRUE;
9386 si->editor.el_headlines = TRUE;
9388 si->editor.show_element_token = FALSE;
9390 si->editor.show_read_only_warning = TRUE;
9392 si->editor.use_template_for_new_levels = TRUE;
9394 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9395 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9396 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9398 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9399 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9400 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9401 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9402 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9404 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9405 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9406 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9407 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9408 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9409 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9411 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9412 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9413 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9415 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9416 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9417 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9418 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9420 for (i = 0; i < MAX_PLAYERS; i++)
9422 si->input[i].use_joystick = FALSE;
9423 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9424 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9425 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9426 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9427 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9428 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9429 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9430 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9431 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9432 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9433 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9434 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9435 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9436 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9437 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9440 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9441 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9442 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9443 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9445 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9446 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9447 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9448 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9449 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9450 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9451 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9453 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9455 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9456 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9457 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9459 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9460 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9461 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9463 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9464 si->internal.choose_from_top_leveldir = FALSE;
9465 si->internal.show_scaling_in_title = TRUE;
9466 si->internal.create_user_levelset = TRUE;
9468 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9469 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9471 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9472 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9473 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9474 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9475 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9476 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9477 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9478 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9479 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9480 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9482 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9483 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9484 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9485 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9486 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9487 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9488 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9489 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9490 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9491 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9493 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9494 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9496 si->debug.show_frames_per_second = FALSE;
9498 si->debug.xsn_mode = AUTO;
9499 si->debug.xsn_percent = 0;
9501 si->options.verbose = FALSE;
9503 #if defined(PLATFORM_ANDROID)
9504 si->fullscreen = TRUE;
9507 setHideSetupEntry(&setup.debug.xsn_mode);
9510 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9512 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9515 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9517 si->editor_cascade.el_bd = TRUE;
9518 si->editor_cascade.el_em = TRUE;
9519 si->editor_cascade.el_emc = TRUE;
9520 si->editor_cascade.el_rnd = TRUE;
9521 si->editor_cascade.el_sb = TRUE;
9522 si->editor_cascade.el_sp = TRUE;
9523 si->editor_cascade.el_dc = TRUE;
9524 si->editor_cascade.el_dx = TRUE;
9526 si->editor_cascade.el_mm = TRUE;
9527 si->editor_cascade.el_df = TRUE;
9529 si->editor_cascade.el_chars = FALSE;
9530 si->editor_cascade.el_steel_chars = FALSE;
9531 si->editor_cascade.el_ce = FALSE;
9532 si->editor_cascade.el_ge = FALSE;
9533 si->editor_cascade.el_ref = FALSE;
9534 si->editor_cascade.el_user = FALSE;
9535 si->editor_cascade.el_dynamic = FALSE;
9538 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9540 static char *getHideSetupToken(void *setup_value)
9542 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9544 if (setup_value != NULL)
9545 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9547 return hide_setup_token;
9550 void setHideSetupEntry(void *setup_value)
9552 char *hide_setup_token = getHideSetupToken(setup_value);
9554 if (hide_setup_hash == NULL)
9555 hide_setup_hash = newSetupFileHash();
9557 if (setup_value != NULL)
9558 setHashEntry(hide_setup_hash, hide_setup_token, "");
9561 void removeHideSetupEntry(void *setup_value)
9563 char *hide_setup_token = getHideSetupToken(setup_value);
9565 if (setup_value != NULL)
9566 removeHashEntry(hide_setup_hash, hide_setup_token);
9569 boolean hideSetupEntry(void *setup_value)
9571 char *hide_setup_token = getHideSetupToken(setup_value);
9573 return (setup_value != NULL &&
9574 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9577 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9578 struct TokenInfo *token_info,
9579 int token_nr, char *token_text)
9581 char *token_hide_text = getStringCat2(token_text, ".hide");
9582 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9584 // set the value of this setup option in the setup option structure
9585 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9587 // check if this setup option should be hidden in the setup menu
9588 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9589 setHideSetupEntry(token_info[token_nr].value);
9591 free(token_hide_text);
9594 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9595 struct TokenInfo *token_info,
9598 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9599 token_info[token_nr].text);
9602 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9606 if (!setup_file_hash)
9609 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9610 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9612 setup.touch.grid_initialized = TRUE;
9613 for (i = 0; i < 2; i++)
9615 int grid_xsize = setup.touch.grid_xsize[i];
9616 int grid_ysize = setup.touch.grid_ysize[i];
9619 // if virtual buttons are not loaded from setup file, repeat initializing
9620 // virtual buttons grid with default values later when video is initialized
9621 if (grid_xsize == -1 ||
9624 setup.touch.grid_initialized = FALSE;
9629 for (y = 0; y < grid_ysize; y++)
9631 char token_string[MAX_LINE_LEN];
9633 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9635 char *value_string = getHashEntry(setup_file_hash, token_string);
9637 if (value_string == NULL)
9640 for (x = 0; x < grid_xsize; x++)
9642 char c = value_string[x];
9644 setup.touch.grid_button[i][x][y] =
9645 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9650 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9651 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9653 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9654 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9656 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9660 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9662 setup_input = setup.input[pnr];
9663 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9665 char full_token[100];
9667 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9668 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9671 setup.input[pnr] = setup_input;
9674 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9675 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9677 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9678 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9680 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9681 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9683 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9684 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9686 setHideRelatedSetupEntries();
9689 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9693 if (!setup_file_hash)
9696 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9697 setSetupInfo(auto_setup_tokens, i,
9698 getHashEntry(setup_file_hash,
9699 auto_setup_tokens[i].text));
9702 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9706 if (!setup_file_hash)
9709 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9710 setSetupInfo(editor_cascade_setup_tokens, i,
9711 getHashEntry(setup_file_hash,
9712 editor_cascade_setup_tokens[i].text));
9715 void LoadUserNames(void)
9717 int last_user_nr = user.nr;
9720 if (global.user_names != NULL)
9722 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9723 checked_free(global.user_names[i]);
9725 checked_free(global.user_names);
9728 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
9730 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9734 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
9736 if (setup_file_hash)
9738 char *player_name = getHashEntry(setup_file_hash, "player_name");
9740 global.user_names[i] = getFixedUserName(player_name);
9742 freeSetupFileHash(setup_file_hash);
9745 if (global.user_names[i] == NULL)
9746 global.user_names[i] = getStringCopy(getDefaultUserName(i));
9749 user.nr = last_user_nr;
9752 void LoadSetupFromFilename(char *filename)
9754 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9756 if (setup_file_hash)
9758 decodeSetupFileHash(setup_file_hash);
9760 freeSetupFileHash(setup_file_hash);
9764 Debug("setup", "using default setup values");
9768 static void LoadSetup_SpecialPostProcessing(void)
9770 char *player_name_new;
9772 // needed to work around problems with fixed length strings
9773 player_name_new = getFixedUserName(setup.player_name);
9774 free(setup.player_name);
9775 setup.player_name = player_name_new;
9777 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9778 if (setup.scroll_delay == FALSE)
9780 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9781 setup.scroll_delay = TRUE; // now always "on"
9784 // make sure that scroll delay value stays inside valid range
9785 setup.scroll_delay_value =
9786 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9789 void LoadSetup(void)
9793 // always start with reliable default values
9794 setSetupInfoToDefaults(&setup);
9796 // try to load setup values from default setup file
9797 filename = getDefaultSetupFilename();
9799 if (fileExists(filename))
9800 LoadSetupFromFilename(filename);
9802 // try to load setup values from user setup file
9803 filename = getSetupFilename();
9805 LoadSetupFromFilename(filename);
9807 LoadSetup_SpecialPostProcessing();
9810 void LoadSetup_AutoSetup(void)
9812 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9813 SetupFileHash *setup_file_hash = NULL;
9815 // always start with reliable default values
9816 setSetupInfoToDefaults_AutoSetup(&setup);
9818 setup_file_hash = loadSetupFileHash(filename);
9820 if (setup_file_hash)
9822 decodeSetupFileHash_AutoSetup(setup_file_hash);
9824 freeSetupFileHash(setup_file_hash);
9830 void LoadSetup_EditorCascade(void)
9832 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9833 SetupFileHash *setup_file_hash = NULL;
9835 // always start with reliable default values
9836 setSetupInfoToDefaults_EditorCascade(&setup);
9838 setup_file_hash = loadSetupFileHash(filename);
9840 if (setup_file_hash)
9842 decodeSetupFileHash_EditorCascade(setup_file_hash);
9844 freeSetupFileHash(setup_file_hash);
9850 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9853 char mapping_guid[MAX_LINE_LEN];
9854 char *mapping_start, *mapping_end;
9856 // get GUID from game controller mapping line: copy complete line
9857 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9858 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9860 // get GUID from game controller mapping line: cut after GUID part
9861 mapping_start = strchr(mapping_guid, ',');
9862 if (mapping_start != NULL)
9863 *mapping_start = '\0';
9865 // cut newline from game controller mapping line
9866 mapping_end = strchr(mapping_line, '\n');
9867 if (mapping_end != NULL)
9868 *mapping_end = '\0';
9870 // add mapping entry to game controller mappings hash
9871 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9874 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9879 if (!(file = fopen(filename, MODE_READ)))
9881 Warn("cannot read game controller mappings file '%s'", filename);
9888 char line[MAX_LINE_LEN];
9890 if (!fgets(line, MAX_LINE_LEN, file))
9893 addGameControllerMappingToHash(mappings_hash, line);
9899 void SaveSetup(void)
9901 char *filename = getSetupFilename();
9905 InitUserDataDirectory();
9907 if (!(file = fopen(filename, MODE_WRITE)))
9909 Warn("cannot write setup file '%s'", filename);
9914 fprintFileHeader(file, SETUP_FILENAME);
9916 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9918 // just to make things nicer :)
9919 if (global_setup_tokens[i].value == &setup.multiple_users ||
9920 global_setup_tokens[i].value == &setup.sound ||
9921 global_setup_tokens[i].value == &setup.graphics_set ||
9922 global_setup_tokens[i].value == &setup.volume_simple ||
9923 global_setup_tokens[i].value == &setup.network_mode ||
9924 global_setup_tokens[i].value == &setup.touch.control_type ||
9925 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
9926 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9927 fprintf(file, "\n");
9929 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9932 for (i = 0; i < 2; i++)
9934 int grid_xsize = setup.touch.grid_xsize[i];
9935 int grid_ysize = setup.touch.grid_ysize[i];
9938 fprintf(file, "\n");
9940 for (y = 0; y < grid_ysize; y++)
9942 char token_string[MAX_LINE_LEN];
9943 char value_string[MAX_LINE_LEN];
9945 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9947 for (x = 0; x < grid_xsize; x++)
9949 char c = setup.touch.grid_button[i][x][y];
9951 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9954 value_string[grid_xsize] = '\0';
9956 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9960 fprintf(file, "\n");
9961 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9962 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9964 fprintf(file, "\n");
9965 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9966 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9968 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9972 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9973 fprintf(file, "\n");
9975 setup_input = setup.input[pnr];
9976 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9977 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9980 fprintf(file, "\n");
9981 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9982 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9984 // (internal setup values not saved to user setup file)
9986 fprintf(file, "\n");
9987 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9988 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
9989 setup.debug.xsn_mode != AUTO)
9990 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9992 fprintf(file, "\n");
9993 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9994 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9998 SetFilePermissions(filename, PERMS_PRIVATE);
10001 void SaveSetup_AutoSetup(void)
10003 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10007 InitUserDataDirectory();
10009 if (!(file = fopen(filename, MODE_WRITE)))
10011 Warn("cannot write auto setup file '%s'", filename);
10018 fprintFileHeader(file, AUTOSETUP_FILENAME);
10020 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10021 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
10025 SetFilePermissions(filename, PERMS_PRIVATE);
10030 void SaveSetup_EditorCascade(void)
10032 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10036 InitUserDataDirectory();
10038 if (!(file = fopen(filename, MODE_WRITE)))
10040 Warn("cannot write editor cascade state file '%s'", filename);
10047 fprintFileHeader(file, EDITORCASCADE_FILENAME);
10049 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10050 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
10054 SetFilePermissions(filename, PERMS_PRIVATE);
10059 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10064 if (!(file = fopen(filename, MODE_WRITE)))
10066 Warn("cannot write game controller mappings file '%s'", filename);
10071 BEGIN_HASH_ITERATION(mappings_hash, itr)
10073 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10075 END_HASH_ITERATION(mappings_hash, itr)
10080 void SaveSetup_AddGameControllerMapping(char *mapping)
10082 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10083 SetupFileHash *mappings_hash = newSetupFileHash();
10085 InitUserDataDirectory();
10087 // load existing personal game controller mappings
10088 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10090 // add new mapping to personal game controller mappings
10091 addGameControllerMappingToHash(mappings_hash, mapping);
10093 // save updated personal game controller mappings
10094 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10096 freeSetupFileHash(mappings_hash);
10100 void LoadCustomElementDescriptions(void)
10102 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10103 SetupFileHash *setup_file_hash;
10106 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10108 if (element_info[i].custom_description != NULL)
10110 free(element_info[i].custom_description);
10111 element_info[i].custom_description = NULL;
10115 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10118 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10120 char *token = getStringCat2(element_info[i].token_name, ".name");
10121 char *value = getHashEntry(setup_file_hash, token);
10124 element_info[i].custom_description = getStringCopy(value);
10129 freeSetupFileHash(setup_file_hash);
10132 static int getElementFromToken(char *token)
10134 char *value = getHashEntry(element_token_hash, token);
10137 return atoi(value);
10139 Warn("unknown element token '%s'", token);
10141 return EL_UNDEFINED;
10144 void FreeGlobalAnimEventInfo(void)
10146 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10148 if (gaei->event_list == NULL)
10153 for (i = 0; i < gaei->num_event_lists; i++)
10155 checked_free(gaei->event_list[i]->event_value);
10156 checked_free(gaei->event_list[i]);
10159 checked_free(gaei->event_list);
10161 gaei->event_list = NULL;
10162 gaei->num_event_lists = 0;
10165 static int AddGlobalAnimEventList(void)
10167 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10168 int list_pos = gaei->num_event_lists++;
10170 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10171 sizeof(struct GlobalAnimEventListInfo *));
10173 gaei->event_list[list_pos] =
10174 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10176 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10178 gaeli->event_value = NULL;
10179 gaeli->num_event_values = 0;
10184 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10186 // do not add empty global animation events
10187 if (event_value == ANIM_EVENT_NONE)
10190 // if list position is undefined, create new list
10191 if (list_pos == ANIM_EVENT_UNDEFINED)
10192 list_pos = AddGlobalAnimEventList();
10194 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10195 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10196 int value_pos = gaeli->num_event_values++;
10198 gaeli->event_value = checked_realloc(gaeli->event_value,
10199 gaeli->num_event_values * sizeof(int *));
10201 gaeli->event_value[value_pos] = event_value;
10206 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10208 if (list_pos == ANIM_EVENT_UNDEFINED)
10209 return ANIM_EVENT_NONE;
10211 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10212 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10214 return gaeli->event_value[value_pos];
10217 int GetGlobalAnimEventValueCount(int list_pos)
10219 if (list_pos == ANIM_EVENT_UNDEFINED)
10222 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10223 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10225 return gaeli->num_event_values;
10228 // This function checks if a string <s> of the format "string1, string2, ..."
10229 // exactly contains a string <s_contained>.
10231 static boolean string_has_parameter(char *s, char *s_contained)
10235 if (s == NULL || s_contained == NULL)
10238 if (strlen(s_contained) > strlen(s))
10241 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10243 char next_char = s[strlen(s_contained)];
10245 // check if next character is delimiter or whitespace
10246 return (next_char == ',' || next_char == '\0' ||
10247 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10250 // check if string contains another parameter string after a comma
10251 substring = strchr(s, ',');
10252 if (substring == NULL) // string does not contain a comma
10255 // advance string pointer to next character after the comma
10258 // skip potential whitespaces after the comma
10259 while (*substring == ' ' || *substring == '\t')
10262 return string_has_parameter(substring, s_contained);
10265 static int get_anim_parameter_value(char *s)
10267 int event_value[] =
10275 char *pattern_1[] =
10283 char *pattern_2 = ".part_";
10284 char *matching_char = NULL;
10286 int pattern_1_len = 0;
10287 int result = ANIM_EVENT_NONE;
10290 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10292 matching_char = strstr(s_ptr, pattern_1[i]);
10293 pattern_1_len = strlen(pattern_1[i]);
10294 result = event_value[i];
10296 if (matching_char != NULL)
10300 if (matching_char == NULL)
10301 return ANIM_EVENT_NONE;
10303 s_ptr = matching_char + pattern_1_len;
10305 // check for main animation number ("anim_X" or "anim_XX")
10306 if (*s_ptr >= '0' && *s_ptr <= '9')
10308 int gic_anim_nr = (*s_ptr++ - '0');
10310 if (*s_ptr >= '0' && *s_ptr <= '9')
10311 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10313 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10314 return ANIM_EVENT_NONE;
10316 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10320 // invalid main animation number specified
10322 return ANIM_EVENT_NONE;
10325 // check for animation part number ("part_X" or "part_XX") (optional)
10326 if (strPrefix(s_ptr, pattern_2))
10328 s_ptr += strlen(pattern_2);
10330 if (*s_ptr >= '0' && *s_ptr <= '9')
10332 int gic_part_nr = (*s_ptr++ - '0');
10334 if (*s_ptr >= '0' && *s_ptr <= '9')
10335 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10337 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10338 return ANIM_EVENT_NONE;
10340 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10344 // invalid animation part number specified
10346 return ANIM_EVENT_NONE;
10350 // discard result if next character is neither delimiter nor whitespace
10351 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10352 *s_ptr == ' ' || *s_ptr == '\t'))
10353 return ANIM_EVENT_NONE;
10358 static int get_anim_parameter_values(char *s)
10360 int list_pos = ANIM_EVENT_UNDEFINED;
10361 int event_value = ANIM_EVENT_DEFAULT;
10363 if (string_has_parameter(s, "any"))
10364 event_value |= ANIM_EVENT_ANY;
10366 if (string_has_parameter(s, "click:self") ||
10367 string_has_parameter(s, "click") ||
10368 string_has_parameter(s, "self"))
10369 event_value |= ANIM_EVENT_SELF;
10371 if (string_has_parameter(s, "unclick:any"))
10372 event_value |= ANIM_EVENT_UNCLICK_ANY;
10374 // if animation event found, add it to global animation event list
10375 if (event_value != ANIM_EVENT_NONE)
10376 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10380 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10381 event_value = get_anim_parameter_value(s);
10383 // if animation event found, add it to global animation event list
10384 if (event_value != ANIM_EVENT_NONE)
10385 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10387 // continue with next part of the string, starting with next comma
10388 s = strchr(s + 1, ',');
10394 static int get_anim_action_parameter_value(char *token)
10396 // check most common default case first to massively speed things up
10397 if (strEqual(token, ARG_UNDEFINED))
10398 return ANIM_EVENT_ACTION_NONE;
10400 int result = getImageIDFromToken(token);
10404 char *gfx_token = getStringCat2("gfx.", token);
10406 result = getImageIDFromToken(gfx_token);
10408 checked_free(gfx_token);
10413 Key key = getKeyFromX11KeyName(token);
10415 if (key != KSYM_UNDEFINED)
10416 result = -(int)key;
10420 result = ANIM_EVENT_ACTION_NONE;
10425 int get_parameter_value(char *value_raw, char *suffix, int type)
10427 char *value = getStringToLower(value_raw);
10428 int result = 0; // probably a save default value
10430 if (strEqual(suffix, ".direction"))
10432 result = (strEqual(value, "left") ? MV_LEFT :
10433 strEqual(value, "right") ? MV_RIGHT :
10434 strEqual(value, "up") ? MV_UP :
10435 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10437 else if (strEqual(suffix, ".position"))
10439 result = (strEqual(value, "left") ? POS_LEFT :
10440 strEqual(value, "right") ? POS_RIGHT :
10441 strEqual(value, "top") ? POS_TOP :
10442 strEqual(value, "upper") ? POS_UPPER :
10443 strEqual(value, "middle") ? POS_MIDDLE :
10444 strEqual(value, "lower") ? POS_LOWER :
10445 strEqual(value, "bottom") ? POS_BOTTOM :
10446 strEqual(value, "any") ? POS_ANY :
10447 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10449 else if (strEqual(suffix, ".align"))
10451 result = (strEqual(value, "left") ? ALIGN_LEFT :
10452 strEqual(value, "right") ? ALIGN_RIGHT :
10453 strEqual(value, "center") ? ALIGN_CENTER :
10454 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10456 else if (strEqual(suffix, ".valign"))
10458 result = (strEqual(value, "top") ? VALIGN_TOP :
10459 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10460 strEqual(value, "middle") ? VALIGN_MIDDLE :
10461 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10463 else if (strEqual(suffix, ".anim_mode"))
10465 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10466 string_has_parameter(value, "loop") ? ANIM_LOOP :
10467 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10468 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10469 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10470 string_has_parameter(value, "random") ? ANIM_RANDOM :
10471 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10472 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10473 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10474 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10475 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10476 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10477 string_has_parameter(value, "all") ? ANIM_ALL :
10480 if (string_has_parameter(value, "once"))
10481 result |= ANIM_ONCE;
10483 if (string_has_parameter(value, "reverse"))
10484 result |= ANIM_REVERSE;
10486 if (string_has_parameter(value, "opaque_player"))
10487 result |= ANIM_OPAQUE_PLAYER;
10489 if (string_has_parameter(value, "static_panel"))
10490 result |= ANIM_STATIC_PANEL;
10492 else if (strEqual(suffix, ".init_event") ||
10493 strEqual(suffix, ".anim_event"))
10495 result = get_anim_parameter_values(value);
10497 else if (strEqual(suffix, ".init_delay_action") ||
10498 strEqual(suffix, ".anim_delay_action") ||
10499 strEqual(suffix, ".post_delay_action") ||
10500 strEqual(suffix, ".init_event_action") ||
10501 strEqual(suffix, ".anim_event_action"))
10503 result = get_anim_action_parameter_value(value_raw);
10505 else if (strEqual(suffix, ".class"))
10507 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10508 get_hash_from_key(value));
10510 else if (strEqual(suffix, ".style"))
10512 result = STYLE_DEFAULT;
10514 if (string_has_parameter(value, "accurate_borders"))
10515 result |= STYLE_ACCURATE_BORDERS;
10517 if (string_has_parameter(value, "inner_corners"))
10518 result |= STYLE_INNER_CORNERS;
10520 if (string_has_parameter(value, "reverse"))
10521 result |= STYLE_REVERSE;
10523 if (string_has_parameter(value, "leftmost_position"))
10524 result |= STYLE_LEFTMOST_POSITION;
10526 if (string_has_parameter(value, "block_clicks"))
10527 result |= STYLE_BLOCK;
10529 if (string_has_parameter(value, "passthrough_clicks"))
10530 result |= STYLE_PASSTHROUGH;
10532 if (string_has_parameter(value, "multiple_actions"))
10533 result |= STYLE_MULTIPLE_ACTIONS;
10535 else if (strEqual(suffix, ".fade_mode"))
10537 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10538 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10539 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10540 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10541 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10542 FADE_MODE_DEFAULT);
10544 else if (strEqual(suffix, ".auto_delay_unit"))
10546 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10547 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10548 AUTO_DELAY_UNIT_DEFAULT);
10550 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10552 result = gfx.get_font_from_token_function(value);
10554 else // generic parameter of type integer or boolean
10556 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10557 type == TYPE_INTEGER ? get_integer_from_string(value) :
10558 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10559 ARG_UNDEFINED_VALUE);
10567 static int get_token_parameter_value(char *token, char *value_raw)
10571 if (token == NULL || value_raw == NULL)
10572 return ARG_UNDEFINED_VALUE;
10574 suffix = strrchr(token, '.');
10575 if (suffix == NULL)
10578 if (strEqual(suffix, ".element"))
10579 return getElementFromToken(value_raw);
10581 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10582 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10585 void InitMenuDesignSettings_Static(void)
10589 // always start with reliable default values from static default config
10590 for (i = 0; image_config_vars[i].token != NULL; i++)
10592 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10595 *image_config_vars[i].value =
10596 get_token_parameter_value(image_config_vars[i].token, value);
10600 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10604 // the following initializes hierarchical values from static configuration
10606 // special case: initialize "ARG_DEFAULT" values in static default config
10607 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10608 titlescreen_initial_first_default.fade_mode =
10609 title_initial_first_default.fade_mode;
10610 titlescreen_initial_first_default.fade_delay =
10611 title_initial_first_default.fade_delay;
10612 titlescreen_initial_first_default.post_delay =
10613 title_initial_first_default.post_delay;
10614 titlescreen_initial_first_default.auto_delay =
10615 title_initial_first_default.auto_delay;
10616 titlescreen_initial_first_default.auto_delay_unit =
10617 title_initial_first_default.auto_delay_unit;
10618 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10619 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10620 titlescreen_first_default.post_delay = title_first_default.post_delay;
10621 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10622 titlescreen_first_default.auto_delay_unit =
10623 title_first_default.auto_delay_unit;
10624 titlemessage_initial_first_default.fade_mode =
10625 title_initial_first_default.fade_mode;
10626 titlemessage_initial_first_default.fade_delay =
10627 title_initial_first_default.fade_delay;
10628 titlemessage_initial_first_default.post_delay =
10629 title_initial_first_default.post_delay;
10630 titlemessage_initial_first_default.auto_delay =
10631 title_initial_first_default.auto_delay;
10632 titlemessage_initial_first_default.auto_delay_unit =
10633 title_initial_first_default.auto_delay_unit;
10634 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10635 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10636 titlemessage_first_default.post_delay = title_first_default.post_delay;
10637 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10638 titlemessage_first_default.auto_delay_unit =
10639 title_first_default.auto_delay_unit;
10641 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10642 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10643 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10644 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10645 titlescreen_initial_default.auto_delay_unit =
10646 title_initial_default.auto_delay_unit;
10647 titlescreen_default.fade_mode = title_default.fade_mode;
10648 titlescreen_default.fade_delay = title_default.fade_delay;
10649 titlescreen_default.post_delay = title_default.post_delay;
10650 titlescreen_default.auto_delay = title_default.auto_delay;
10651 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10652 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10653 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10654 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10655 titlemessage_initial_default.auto_delay_unit =
10656 title_initial_default.auto_delay_unit;
10657 titlemessage_default.fade_mode = title_default.fade_mode;
10658 titlemessage_default.fade_delay = title_default.fade_delay;
10659 titlemessage_default.post_delay = title_default.post_delay;
10660 titlemessage_default.auto_delay = title_default.auto_delay;
10661 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10663 // special case: initialize "ARG_DEFAULT" values in static default config
10664 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10665 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10667 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10668 titlescreen_first[i] = titlescreen_first_default;
10669 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10670 titlemessage_first[i] = titlemessage_first_default;
10672 titlescreen_initial[i] = titlescreen_initial_default;
10673 titlescreen[i] = titlescreen_default;
10674 titlemessage_initial[i] = titlemessage_initial_default;
10675 titlemessage[i] = titlemessage_default;
10678 // special case: initialize "ARG_DEFAULT" values in static default config
10679 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10680 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10682 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
10685 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10686 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10687 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10690 // special case: initialize "ARG_DEFAULT" values in static default config
10691 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10692 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10694 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10695 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10696 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10698 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
10701 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10705 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10709 struct XY *dst, *src;
10711 game_buttons_xy[] =
10713 { &game.button.save, &game.button.stop },
10714 { &game.button.pause2, &game.button.pause },
10715 { &game.button.load, &game.button.play },
10716 { &game.button.undo, &game.button.stop },
10717 { &game.button.redo, &game.button.play },
10723 // special case: initialize later added SETUP list size from LEVELS value
10724 if (menu.list_size[GAME_MODE_SETUP] == -1)
10725 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10727 // set default position for snapshot buttons to stop/pause/play buttons
10728 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10729 if ((*game_buttons_xy[i].dst).x == -1 &&
10730 (*game_buttons_xy[i].dst).y == -1)
10731 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10733 // --------------------------------------------------------------------------
10734 // dynamic viewports (including playfield margins, borders and alignments)
10735 // --------------------------------------------------------------------------
10737 // dynamic viewports currently only supported for landscape mode
10738 int display_width = MAX(video.display_width, video.display_height);
10739 int display_height = MIN(video.display_width, video.display_height);
10741 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10743 struct RectWithBorder *vp_window = &viewport.window[i];
10744 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10745 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
10746 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
10747 boolean dynamic_window_width = (vp_window->min_width != -1);
10748 boolean dynamic_window_height = (vp_window->min_height != -1);
10749 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
10750 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10752 // adjust window size if min/max width/height is specified
10754 if (vp_window->min_width != -1)
10756 int window_width = display_width;
10758 // when using static window height, use aspect ratio of display
10759 if (vp_window->min_height == -1)
10760 window_width = vp_window->height * display_width / display_height;
10762 vp_window->width = MAX(vp_window->min_width, window_width);
10765 if (vp_window->min_height != -1)
10767 int window_height = display_height;
10769 // when using static window width, use aspect ratio of display
10770 if (vp_window->min_width == -1)
10771 window_height = vp_window->width * display_height / display_width;
10773 vp_window->height = MAX(vp_window->min_height, window_height);
10776 if (vp_window->max_width != -1)
10777 vp_window->width = MIN(vp_window->width, vp_window->max_width);
10779 if (vp_window->max_height != -1)
10780 vp_window->height = MIN(vp_window->height, vp_window->max_height);
10782 int playfield_width = vp_window->width;
10783 int playfield_height = vp_window->height;
10785 // adjust playfield size and position according to specified margins
10787 playfield_width -= vp_playfield->margin_left;
10788 playfield_width -= vp_playfield->margin_right;
10790 playfield_height -= vp_playfield->margin_top;
10791 playfield_height -= vp_playfield->margin_bottom;
10793 // adjust playfield size if min/max width/height is specified
10795 if (vp_playfield->min_width != -1)
10796 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10798 if (vp_playfield->min_height != -1)
10799 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10801 if (vp_playfield->max_width != -1)
10802 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10804 if (vp_playfield->max_height != -1)
10805 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10807 // adjust playfield position according to specified alignment
10809 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10810 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10811 else if (vp_playfield->align == ALIGN_CENTER)
10812 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10813 else if (vp_playfield->align == ALIGN_RIGHT)
10814 vp_playfield->x += playfield_width - vp_playfield->width;
10816 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10817 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10818 else if (vp_playfield->valign == VALIGN_MIDDLE)
10819 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10820 else if (vp_playfield->valign == VALIGN_BOTTOM)
10821 vp_playfield->y += playfield_height - vp_playfield->height;
10823 vp_playfield->x += vp_playfield->margin_left;
10824 vp_playfield->y += vp_playfield->margin_top;
10826 // adjust individual playfield borders if only default border is specified
10828 if (vp_playfield->border_left == -1)
10829 vp_playfield->border_left = vp_playfield->border_size;
10830 if (vp_playfield->border_right == -1)
10831 vp_playfield->border_right = vp_playfield->border_size;
10832 if (vp_playfield->border_top == -1)
10833 vp_playfield->border_top = vp_playfield->border_size;
10834 if (vp_playfield->border_bottom == -1)
10835 vp_playfield->border_bottom = vp_playfield->border_size;
10837 // set dynamic playfield borders if borders are specified as undefined
10838 // (but only if window size was dynamic and playfield size was static)
10840 if (dynamic_window_width && !dynamic_playfield_width)
10842 if (vp_playfield->border_left == -1)
10844 vp_playfield->border_left = (vp_playfield->x -
10845 vp_playfield->margin_left);
10846 vp_playfield->x -= vp_playfield->border_left;
10847 vp_playfield->width += vp_playfield->border_left;
10850 if (vp_playfield->border_right == -1)
10852 vp_playfield->border_right = (vp_window->width -
10854 vp_playfield->width -
10855 vp_playfield->margin_right);
10856 vp_playfield->width += vp_playfield->border_right;
10860 if (dynamic_window_height && !dynamic_playfield_height)
10862 if (vp_playfield->border_top == -1)
10864 vp_playfield->border_top = (vp_playfield->y -
10865 vp_playfield->margin_top);
10866 vp_playfield->y -= vp_playfield->border_top;
10867 vp_playfield->height += vp_playfield->border_top;
10870 if (vp_playfield->border_bottom == -1)
10872 vp_playfield->border_bottom = (vp_window->height -
10874 vp_playfield->height -
10875 vp_playfield->margin_bottom);
10876 vp_playfield->height += vp_playfield->border_bottom;
10880 // adjust playfield size to be a multiple of a defined alignment tile size
10882 int align_size = vp_playfield->align_size;
10883 int playfield_xtiles = vp_playfield->width / align_size;
10884 int playfield_ytiles = vp_playfield->height / align_size;
10885 int playfield_width_corrected = playfield_xtiles * align_size;
10886 int playfield_height_corrected = playfield_ytiles * align_size;
10887 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10888 i == GFX_SPECIAL_ARG_EDITOR);
10890 if (is_playfield_mode &&
10891 dynamic_playfield_width &&
10892 vp_playfield->width != playfield_width_corrected)
10894 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10896 vp_playfield->width = playfield_width_corrected;
10898 if (vp_playfield->align == ALIGN_LEFT)
10900 vp_playfield->border_left += playfield_xdiff;
10902 else if (vp_playfield->align == ALIGN_RIGHT)
10904 vp_playfield->border_right += playfield_xdiff;
10906 else if (vp_playfield->align == ALIGN_CENTER)
10908 int border_left_diff = playfield_xdiff / 2;
10909 int border_right_diff = playfield_xdiff - border_left_diff;
10911 vp_playfield->border_left += border_left_diff;
10912 vp_playfield->border_right += border_right_diff;
10916 if (is_playfield_mode &&
10917 dynamic_playfield_height &&
10918 vp_playfield->height != playfield_height_corrected)
10920 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10922 vp_playfield->height = playfield_height_corrected;
10924 if (vp_playfield->valign == VALIGN_TOP)
10926 vp_playfield->border_top += playfield_ydiff;
10928 else if (vp_playfield->align == VALIGN_BOTTOM)
10930 vp_playfield->border_right += playfield_ydiff;
10932 else if (vp_playfield->align == VALIGN_MIDDLE)
10934 int border_top_diff = playfield_ydiff / 2;
10935 int border_bottom_diff = playfield_ydiff - border_top_diff;
10937 vp_playfield->border_top += border_top_diff;
10938 vp_playfield->border_bottom += border_bottom_diff;
10942 // adjust door positions according to specified alignment
10944 for (j = 0; j < 2; j++)
10946 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10948 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10949 vp_door->x = ALIGNED_VP_XPOS(vp_door);
10950 else if (vp_door->align == ALIGN_CENTER)
10951 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10952 else if (vp_door->align == ALIGN_RIGHT)
10953 vp_door->x += vp_window->width - vp_door->width;
10955 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10956 vp_door->y = ALIGNED_VP_YPOS(vp_door);
10957 else if (vp_door->valign == VALIGN_MIDDLE)
10958 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10959 else if (vp_door->valign == VALIGN_BOTTOM)
10960 vp_door->y += vp_window->height - vp_door->height;
10965 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10969 struct XYTileSize *dst, *src;
10972 editor_buttons_xy[] =
10975 &editor.button.element_left, &editor.palette.element_left,
10976 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10979 &editor.button.element_middle, &editor.palette.element_middle,
10980 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10983 &editor.button.element_right, &editor.palette.element_right,
10984 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10991 // set default position for element buttons to element graphics
10992 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10994 if ((*editor_buttons_xy[i].dst).x == -1 &&
10995 (*editor_buttons_xy[i].dst).y == -1)
10997 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10999 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
11001 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
11005 // adjust editor palette rows and columns if specified to be dynamic
11007 if (editor.palette.cols == -1)
11009 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
11010 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
11011 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
11013 editor.palette.cols = (vp_width - sc_width) / bt_width;
11015 if (editor.palette.x == -1)
11017 int palette_width = editor.palette.cols * bt_width + sc_width;
11019 editor.palette.x = (vp_width - palette_width) / 2;
11023 if (editor.palette.rows == -1)
11025 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
11026 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
11027 int tx_height = getFontHeight(FONT_TEXT_2);
11029 editor.palette.rows = (vp_height - tx_height) / bt_height;
11031 if (editor.palette.y == -1)
11033 int palette_height = editor.palette.rows * bt_height + tx_height;
11035 editor.palette.y = (vp_height - palette_height) / 2;
11040 static void LoadMenuDesignSettingsFromFilename(char *filename)
11042 static struct TitleFadingInfo tfi;
11043 static struct TitleMessageInfo tmi;
11044 static struct TokenInfo title_tokens[] =
11046 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
11047 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
11048 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
11049 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
11050 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
11054 static struct TokenInfo titlemessage_tokens[] =
11056 { TYPE_INTEGER, &tmi.x, ".x" },
11057 { TYPE_INTEGER, &tmi.y, ".y" },
11058 { TYPE_INTEGER, &tmi.width, ".width" },
11059 { TYPE_INTEGER, &tmi.height, ".height" },
11060 { TYPE_INTEGER, &tmi.chars, ".chars" },
11061 { TYPE_INTEGER, &tmi.lines, ".lines" },
11062 { TYPE_INTEGER, &tmi.align, ".align" },
11063 { TYPE_INTEGER, &tmi.valign, ".valign" },
11064 { TYPE_INTEGER, &tmi.font, ".font" },
11065 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
11066 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
11067 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
11068 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
11069 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
11070 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
11071 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
11072 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
11073 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
11079 struct TitleFadingInfo *info;
11084 // initialize first titles from "enter screen" definitions, if defined
11085 { &title_initial_first_default, "menu.enter_screen.TITLE" },
11086 { &title_first_default, "menu.enter_screen.TITLE" },
11088 // initialize title screens from "next screen" definitions, if defined
11089 { &title_initial_default, "menu.next_screen.TITLE" },
11090 { &title_default, "menu.next_screen.TITLE" },
11096 struct TitleMessageInfo *array;
11099 titlemessage_arrays[] =
11101 // initialize first titles from "enter screen" definitions, if defined
11102 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
11103 { titlescreen_first, "menu.enter_screen.TITLE" },
11104 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
11105 { titlemessage_first, "menu.enter_screen.TITLE" },
11107 // initialize titles from "next screen" definitions, if defined
11108 { titlescreen_initial, "menu.next_screen.TITLE" },
11109 { titlescreen, "menu.next_screen.TITLE" },
11110 { titlemessage_initial, "menu.next_screen.TITLE" },
11111 { titlemessage, "menu.next_screen.TITLE" },
11113 // overwrite titles with title definitions, if defined
11114 { titlescreen_initial_first, "[title_initial]" },
11115 { titlescreen_first, "[title]" },
11116 { titlemessage_initial_first, "[title_initial]" },
11117 { titlemessage_first, "[title]" },
11119 { titlescreen_initial, "[title_initial]" },
11120 { titlescreen, "[title]" },
11121 { titlemessage_initial, "[title_initial]" },
11122 { titlemessage, "[title]" },
11124 // overwrite titles with title screen/message definitions, if defined
11125 { titlescreen_initial_first, "[titlescreen_initial]" },
11126 { titlescreen_first, "[titlescreen]" },
11127 { titlemessage_initial_first, "[titlemessage_initial]" },
11128 { titlemessage_first, "[titlemessage]" },
11130 { titlescreen_initial, "[titlescreen_initial]" },
11131 { titlescreen, "[titlescreen]" },
11132 { titlemessage_initial, "[titlemessage_initial]" },
11133 { titlemessage, "[titlemessage]" },
11137 SetupFileHash *setup_file_hash;
11140 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11143 // the following initializes hierarchical values from dynamic configuration
11145 // special case: initialize with default values that may be overwritten
11146 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11147 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11149 struct TokenIntPtrInfo menu_config[] =
11151 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
11152 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
11153 { "menu.list_size", &menu.list_size[i] }
11156 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11158 char *token = menu_config[j].token;
11159 char *value = getHashEntry(setup_file_hash, token);
11162 *menu_config[j].value = get_integer_from_string(value);
11166 // special case: initialize with default values that may be overwritten
11167 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11168 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11170 struct TokenIntPtrInfo menu_config[] =
11172 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
11173 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
11174 { "menu.list_size.INFO", &menu.list_size_info[i] }
11177 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11179 char *token = menu_config[j].token;
11180 char *value = getHashEntry(setup_file_hash, token);
11183 *menu_config[j].value = get_integer_from_string(value);
11187 // special case: initialize with default values that may be overwritten
11188 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11189 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11191 struct TokenIntPtrInfo menu_config[] =
11193 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
11194 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
11197 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11199 char *token = menu_config[j].token;
11200 char *value = getHashEntry(setup_file_hash, token);
11203 *menu_config[j].value = get_integer_from_string(value);
11207 // special case: initialize with default values that may be overwritten
11208 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11209 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11211 struct TokenIntPtrInfo menu_config[] =
11213 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11214 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11215 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11216 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11217 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11218 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11219 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11220 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11221 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11224 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11226 char *token = menu_config[j].token;
11227 char *value = getHashEntry(setup_file_hash, token);
11230 *menu_config[j].value = get_integer_from_string(value);
11234 // special case: initialize with default values that may be overwritten
11235 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11236 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11238 struct TokenIntPtrInfo menu_config[] =
11240 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11241 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11242 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11243 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11244 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11245 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11246 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11247 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11248 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11251 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11253 char *token = menu_config[j].token;
11254 char *value = getHashEntry(setup_file_hash, token);
11257 *menu_config[j].value = get_token_parameter_value(token, value);
11261 // special case: initialize with default values that may be overwritten
11262 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11263 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11267 char *token_prefix;
11268 struct RectWithBorder *struct_ptr;
11272 { "viewport.window", &viewport.window[i] },
11273 { "viewport.playfield", &viewport.playfield[i] },
11274 { "viewport.door_1", &viewport.door_1[i] },
11275 { "viewport.door_2", &viewport.door_2[i] }
11278 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11280 struct TokenIntPtrInfo vp_config[] =
11282 { ".x", &vp_struct[j].struct_ptr->x },
11283 { ".y", &vp_struct[j].struct_ptr->y },
11284 { ".width", &vp_struct[j].struct_ptr->width },
11285 { ".height", &vp_struct[j].struct_ptr->height },
11286 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11287 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11288 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11289 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11290 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11291 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11292 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11293 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11294 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11295 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11296 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11297 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11298 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11299 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11300 { ".align", &vp_struct[j].struct_ptr->align },
11301 { ".valign", &vp_struct[j].struct_ptr->valign }
11304 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11306 char *token = getStringCat2(vp_struct[j].token_prefix,
11307 vp_config[k].token);
11308 char *value = getHashEntry(setup_file_hash, token);
11311 *vp_config[k].value = get_token_parameter_value(token, value);
11318 // special case: initialize with default values that may be overwritten
11319 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11320 for (i = 0; title_info[i].info != NULL; i++)
11322 struct TitleFadingInfo *info = title_info[i].info;
11323 char *base_token = title_info[i].text;
11325 for (j = 0; title_tokens[j].type != -1; j++)
11327 char *token = getStringCat2(base_token, title_tokens[j].text);
11328 char *value = getHashEntry(setup_file_hash, token);
11332 int parameter_value = get_token_parameter_value(token, value);
11336 *(int *)title_tokens[j].value = (int)parameter_value;
11345 // special case: initialize with default values that may be overwritten
11346 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11347 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11349 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11350 char *base_token = titlemessage_arrays[i].text;
11352 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11354 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11355 char *value = getHashEntry(setup_file_hash, token);
11359 int parameter_value = get_token_parameter_value(token, value);
11361 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11365 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11366 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11368 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11378 // special case: check if network and preview player positions are redefined,
11379 // to compare this later against the main menu level preview being redefined
11380 struct TokenIntPtrInfo menu_config_players[] =
11382 { "main.network_players.x", &menu.main.network_players.redefined },
11383 { "main.network_players.y", &menu.main.network_players.redefined },
11384 { "main.preview_players.x", &menu.main.preview_players.redefined },
11385 { "main.preview_players.y", &menu.main.preview_players.redefined },
11386 { "preview.x", &preview.redefined },
11387 { "preview.y", &preview.redefined }
11390 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11391 *menu_config_players[i].value = FALSE;
11393 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11394 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11395 *menu_config_players[i].value = TRUE;
11397 // read (and overwrite with) values that may be specified in config file
11398 for (i = 0; image_config_vars[i].token != NULL; i++)
11400 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11402 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11403 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11404 *image_config_vars[i].value =
11405 get_token_parameter_value(image_config_vars[i].token, value);
11408 freeSetupFileHash(setup_file_hash);
11411 void LoadMenuDesignSettings(void)
11413 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11415 InitMenuDesignSettings_Static();
11416 InitMenuDesignSettings_SpecialPreProcessing();
11418 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11420 // first look for special settings configured in level series config
11421 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11423 if (fileExists(filename_base))
11424 LoadMenuDesignSettingsFromFilename(filename_base);
11427 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11429 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11430 LoadMenuDesignSettingsFromFilename(filename_local);
11432 InitMenuDesignSettings_SpecialPostProcessing();
11435 void LoadMenuDesignSettings_AfterGraphics(void)
11437 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11440 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11442 char *filename = getEditorSetupFilename();
11443 SetupFileList *setup_file_list, *list;
11444 SetupFileHash *element_hash;
11445 int num_unknown_tokens = 0;
11448 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11451 element_hash = newSetupFileHash();
11453 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11454 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11456 // determined size may be larger than needed (due to unknown elements)
11458 for (list = setup_file_list; list != NULL; list = list->next)
11461 // add space for up to 3 more elements for padding that may be needed
11462 *num_elements += 3;
11464 // free memory for old list of elements, if needed
11465 checked_free(*elements);
11467 // allocate memory for new list of elements
11468 *elements = checked_malloc(*num_elements * sizeof(int));
11471 for (list = setup_file_list; list != NULL; list = list->next)
11473 char *value = getHashEntry(element_hash, list->token);
11475 if (value == NULL) // try to find obsolete token mapping
11477 char *mapped_token = get_mapped_token(list->token);
11479 if (mapped_token != NULL)
11481 value = getHashEntry(element_hash, mapped_token);
11483 free(mapped_token);
11489 (*elements)[(*num_elements)++] = atoi(value);
11493 if (num_unknown_tokens == 0)
11496 Warn("unknown token(s) found in config file:");
11497 Warn("- config file: '%s'", filename);
11499 num_unknown_tokens++;
11502 Warn("- token: '%s'", list->token);
11506 if (num_unknown_tokens > 0)
11509 while (*num_elements % 4) // pad with empty elements, if needed
11510 (*elements)[(*num_elements)++] = EL_EMPTY;
11512 freeSetupFileList(setup_file_list);
11513 freeSetupFileHash(element_hash);
11516 for (i = 0; i < *num_elements; i++)
11517 Debug("editor", "element '%s' [%d]\n",
11518 element_info[(*elements)[i]].token_name, (*elements)[i]);
11522 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11525 SetupFileHash *setup_file_hash = NULL;
11526 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11527 char *filename_music, *filename_prefix, *filename_info;
11533 token_to_value_ptr[] =
11535 { "title_header", &tmp_music_file_info.title_header },
11536 { "artist_header", &tmp_music_file_info.artist_header },
11537 { "album_header", &tmp_music_file_info.album_header },
11538 { "year_header", &tmp_music_file_info.year_header },
11540 { "title", &tmp_music_file_info.title },
11541 { "artist", &tmp_music_file_info.artist },
11542 { "album", &tmp_music_file_info.album },
11543 { "year", &tmp_music_file_info.year },
11549 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11550 getCustomMusicFilename(basename));
11552 if (filename_music == NULL)
11555 // ---------- try to replace file extension ----------
11557 filename_prefix = getStringCopy(filename_music);
11558 if (strrchr(filename_prefix, '.') != NULL)
11559 *strrchr(filename_prefix, '.') = '\0';
11560 filename_info = getStringCat2(filename_prefix, ".txt");
11562 if (fileExists(filename_info))
11563 setup_file_hash = loadSetupFileHash(filename_info);
11565 free(filename_prefix);
11566 free(filename_info);
11568 if (setup_file_hash == NULL)
11570 // ---------- try to add file extension ----------
11572 filename_prefix = getStringCopy(filename_music);
11573 filename_info = getStringCat2(filename_prefix, ".txt");
11575 if (fileExists(filename_info))
11576 setup_file_hash = loadSetupFileHash(filename_info);
11578 free(filename_prefix);
11579 free(filename_info);
11582 if (setup_file_hash == NULL)
11585 // ---------- music file info found ----------
11587 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11589 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11591 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11593 *token_to_value_ptr[i].value_ptr =
11594 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11597 tmp_music_file_info.basename = getStringCopy(basename);
11598 tmp_music_file_info.music = music;
11599 tmp_music_file_info.is_sound = is_sound;
11601 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11602 *new_music_file_info = tmp_music_file_info;
11604 return new_music_file_info;
11607 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11609 return get_music_file_info_ext(basename, music, FALSE);
11612 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11614 return get_music_file_info_ext(basename, sound, TRUE);
11617 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11618 char *basename, boolean is_sound)
11620 for (; list != NULL; list = list->next)
11621 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11627 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11629 return music_info_listed_ext(list, basename, FALSE);
11632 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11634 return music_info_listed_ext(list, basename, TRUE);
11637 void LoadMusicInfo(void)
11639 char *music_directory = getCustomMusicDirectory();
11640 int num_music = getMusicListSize();
11641 int num_music_noconf = 0;
11642 int num_sounds = getSoundListSize();
11644 DirectoryEntry *dir_entry;
11645 struct FileInfo *music, *sound;
11646 struct MusicFileInfo *next, **new;
11649 while (music_file_info != NULL)
11651 next = music_file_info->next;
11653 checked_free(music_file_info->basename);
11655 checked_free(music_file_info->title_header);
11656 checked_free(music_file_info->artist_header);
11657 checked_free(music_file_info->album_header);
11658 checked_free(music_file_info->year_header);
11660 checked_free(music_file_info->title);
11661 checked_free(music_file_info->artist);
11662 checked_free(music_file_info->album);
11663 checked_free(music_file_info->year);
11665 free(music_file_info);
11667 music_file_info = next;
11670 new = &music_file_info;
11672 for (i = 0; i < num_music; i++)
11674 music = getMusicListEntry(i);
11676 if (music->filename == NULL)
11679 if (strEqual(music->filename, UNDEFINED_FILENAME))
11682 // a configured file may be not recognized as music
11683 if (!FileIsMusic(music->filename))
11686 if (!music_info_listed(music_file_info, music->filename))
11688 *new = get_music_file_info(music->filename, i);
11691 new = &(*new)->next;
11695 if ((dir = openDirectory(music_directory)) == NULL)
11697 Warn("cannot read music directory '%s'", music_directory);
11702 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
11704 char *basename = dir_entry->basename;
11705 boolean music_already_used = FALSE;
11708 // skip all music files that are configured in music config file
11709 for (i = 0; i < num_music; i++)
11711 music = getMusicListEntry(i);
11713 if (music->filename == NULL)
11716 if (strEqual(basename, music->filename))
11718 music_already_used = TRUE;
11723 if (music_already_used)
11726 if (!FileIsMusic(dir_entry->filename))
11729 if (!music_info_listed(music_file_info, basename))
11731 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11734 new = &(*new)->next;
11737 num_music_noconf++;
11740 closeDirectory(dir);
11742 for (i = 0; i < num_sounds; i++)
11744 sound = getSoundListEntry(i);
11746 if (sound->filename == NULL)
11749 if (strEqual(sound->filename, UNDEFINED_FILENAME))
11752 // a configured file may be not recognized as sound
11753 if (!FileIsSound(sound->filename))
11756 if (!sound_info_listed(music_file_info, sound->filename))
11758 *new = get_sound_file_info(sound->filename, i);
11760 new = &(*new)->next;
11765 static void add_helpanim_entry(int element, int action, int direction,
11766 int delay, int *num_list_entries)
11768 struct HelpAnimInfo *new_list_entry;
11769 (*num_list_entries)++;
11772 checked_realloc(helpanim_info,
11773 *num_list_entries * sizeof(struct HelpAnimInfo));
11774 new_list_entry = &helpanim_info[*num_list_entries - 1];
11776 new_list_entry->element = element;
11777 new_list_entry->action = action;
11778 new_list_entry->direction = direction;
11779 new_list_entry->delay = delay;
11782 static void print_unknown_token(char *filename, char *token, int token_nr)
11787 Warn("unknown token(s) found in config file:");
11788 Warn("- config file: '%s'", filename);
11791 Warn("- token: '%s'", token);
11794 static void print_unknown_token_end(int token_nr)
11800 void LoadHelpAnimInfo(void)
11802 char *filename = getHelpAnimFilename();
11803 SetupFileList *setup_file_list = NULL, *list;
11804 SetupFileHash *element_hash, *action_hash, *direction_hash;
11805 int num_list_entries = 0;
11806 int num_unknown_tokens = 0;
11809 if (fileExists(filename))
11810 setup_file_list = loadSetupFileList(filename);
11812 if (setup_file_list == NULL)
11814 // use reliable default values from static configuration
11815 SetupFileList *insert_ptr;
11817 insert_ptr = setup_file_list =
11818 newSetupFileList(helpanim_config[0].token,
11819 helpanim_config[0].value);
11821 for (i = 1; helpanim_config[i].token; i++)
11822 insert_ptr = addListEntry(insert_ptr,
11823 helpanim_config[i].token,
11824 helpanim_config[i].value);
11827 element_hash = newSetupFileHash();
11828 action_hash = newSetupFileHash();
11829 direction_hash = newSetupFileHash();
11831 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11832 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11834 for (i = 0; i < NUM_ACTIONS; i++)
11835 setHashEntry(action_hash, element_action_info[i].suffix,
11836 i_to_a(element_action_info[i].value));
11838 // do not store direction index (bit) here, but direction value!
11839 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11840 setHashEntry(direction_hash, element_direction_info[i].suffix,
11841 i_to_a(1 << element_direction_info[i].value));
11843 for (list = setup_file_list; list != NULL; list = list->next)
11845 char *element_token, *action_token, *direction_token;
11846 char *element_value, *action_value, *direction_value;
11847 int delay = atoi(list->value);
11849 if (strEqual(list->token, "end"))
11851 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11856 /* first try to break element into element/action/direction parts;
11857 if this does not work, also accept combined "element[.act][.dir]"
11858 elements (like "dynamite.active"), which are unique elements */
11860 if (strchr(list->token, '.') == NULL) // token contains no '.'
11862 element_value = getHashEntry(element_hash, list->token);
11863 if (element_value != NULL) // element found
11864 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11865 &num_list_entries);
11868 // no further suffixes found -- this is not an element
11869 print_unknown_token(filename, list->token, num_unknown_tokens++);
11875 // token has format "<prefix>.<something>"
11877 action_token = strchr(list->token, '.'); // suffix may be action ...
11878 direction_token = action_token; // ... or direction
11880 element_token = getStringCopy(list->token);
11881 *strchr(element_token, '.') = '\0';
11883 element_value = getHashEntry(element_hash, element_token);
11885 if (element_value == NULL) // this is no element
11887 element_value = getHashEntry(element_hash, list->token);
11888 if (element_value != NULL) // combined element found
11889 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11890 &num_list_entries);
11892 print_unknown_token(filename, list->token, num_unknown_tokens++);
11894 free(element_token);
11899 action_value = getHashEntry(action_hash, action_token);
11901 if (action_value != NULL) // action found
11903 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11904 &num_list_entries);
11906 free(element_token);
11911 direction_value = getHashEntry(direction_hash, direction_token);
11913 if (direction_value != NULL) // direction found
11915 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11916 &num_list_entries);
11918 free(element_token);
11923 if (strchr(action_token + 1, '.') == NULL)
11925 // no further suffixes found -- this is not an action nor direction
11927 element_value = getHashEntry(element_hash, list->token);
11928 if (element_value != NULL) // combined element found
11929 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11930 &num_list_entries);
11932 print_unknown_token(filename, list->token, num_unknown_tokens++);
11934 free(element_token);
11939 // token has format "<prefix>.<suffix>.<something>"
11941 direction_token = strchr(action_token + 1, '.');
11943 action_token = getStringCopy(action_token);
11944 *strchr(action_token + 1, '.') = '\0';
11946 action_value = getHashEntry(action_hash, action_token);
11948 if (action_value == NULL) // this is no action
11950 element_value = getHashEntry(element_hash, list->token);
11951 if (element_value != NULL) // combined element found
11952 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11953 &num_list_entries);
11955 print_unknown_token(filename, list->token, num_unknown_tokens++);
11957 free(element_token);
11958 free(action_token);
11963 direction_value = getHashEntry(direction_hash, direction_token);
11965 if (direction_value != NULL) // direction found
11967 add_helpanim_entry(atoi(element_value), atoi(action_value),
11968 atoi(direction_value), delay, &num_list_entries);
11970 free(element_token);
11971 free(action_token);
11976 // this is no direction
11978 element_value = getHashEntry(element_hash, list->token);
11979 if (element_value != NULL) // combined element found
11980 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11981 &num_list_entries);
11983 print_unknown_token(filename, list->token, num_unknown_tokens++);
11985 free(element_token);
11986 free(action_token);
11989 print_unknown_token_end(num_unknown_tokens);
11991 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11992 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
11994 freeSetupFileList(setup_file_list);
11995 freeSetupFileHash(element_hash);
11996 freeSetupFileHash(action_hash);
11997 freeSetupFileHash(direction_hash);
12000 for (i = 0; i < num_list_entries; i++)
12001 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
12002 EL_NAME(helpanim_info[i].element),
12003 helpanim_info[i].element,
12004 helpanim_info[i].action,
12005 helpanim_info[i].direction,
12006 helpanim_info[i].delay);
12010 void LoadHelpTextInfo(void)
12012 char *filename = getHelpTextFilename();
12015 if (helptext_info != NULL)
12017 freeSetupFileHash(helptext_info);
12018 helptext_info = NULL;
12021 if (fileExists(filename))
12022 helptext_info = loadSetupFileHash(filename);
12024 if (helptext_info == NULL)
12026 // use reliable default values from static configuration
12027 helptext_info = newSetupFileHash();
12029 for (i = 0; helptext_config[i].token; i++)
12030 setHashEntry(helptext_info,
12031 helptext_config[i].token,
12032 helptext_config[i].value);
12036 BEGIN_HASH_ITERATION(helptext_info, itr)
12038 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
12039 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
12041 END_HASH_ITERATION(hash, itr)
12046 // ----------------------------------------------------------------------------
12048 // ----------------------------------------------------------------------------
12050 #define MAX_NUM_CONVERT_LEVELS 1000
12052 void ConvertLevels(void)
12054 static LevelDirTree *convert_leveldir = NULL;
12055 static int convert_level_nr = -1;
12056 static int num_levels_handled = 0;
12057 static int num_levels_converted = 0;
12058 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
12061 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12062 global.convert_leveldir);
12064 if (convert_leveldir == NULL)
12065 Fail("no such level identifier: '%s'", global.convert_leveldir);
12067 leveldir_current = convert_leveldir;
12069 if (global.convert_level_nr != -1)
12071 convert_leveldir->first_level = global.convert_level_nr;
12072 convert_leveldir->last_level = global.convert_level_nr;
12075 convert_level_nr = convert_leveldir->first_level;
12077 PrintLine("=", 79);
12078 Print("Converting levels\n");
12079 PrintLine("-", 79);
12080 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12081 Print("Level series name: '%s'\n", convert_leveldir->name);
12082 Print("Level series author: '%s'\n", convert_leveldir->author);
12083 Print("Number of levels: %d\n", convert_leveldir->levels);
12084 PrintLine("=", 79);
12087 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12088 levels_failed[i] = FALSE;
12090 while (convert_level_nr <= convert_leveldir->last_level)
12092 char *level_filename;
12095 level_nr = convert_level_nr++;
12097 Print("Level %03d: ", level_nr);
12099 LoadLevel(level_nr);
12100 if (level.no_level_file || level.no_valid_file)
12102 Print("(no level)\n");
12106 Print("converting level ... ");
12108 level_filename = getDefaultLevelFilename(level_nr);
12109 new_level = !fileExists(level_filename);
12113 SaveLevel(level_nr);
12115 num_levels_converted++;
12117 Print("converted.\n");
12121 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12122 levels_failed[level_nr] = TRUE;
12124 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12127 num_levels_handled++;
12131 PrintLine("=", 79);
12132 Print("Number of levels handled: %d\n", num_levels_handled);
12133 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12134 (num_levels_handled ?
12135 num_levels_converted * 100 / num_levels_handled : 0));
12136 PrintLine("-", 79);
12137 Print("Summary (for automatic parsing by scripts):\n");
12138 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12139 convert_leveldir->identifier, num_levels_converted,
12140 num_levels_handled,
12141 (num_levels_handled ?
12142 num_levels_converted * 100 / num_levels_handled : 0));
12144 if (num_levels_handled != num_levels_converted)
12146 Print(", FAILED:");
12147 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12148 if (levels_failed[i])
12153 PrintLine("=", 79);
12155 CloseAllAndExit(0);
12159 // ----------------------------------------------------------------------------
12160 // create and save images for use in level sketches (raw BMP format)
12161 // ----------------------------------------------------------------------------
12163 void CreateLevelSketchImages(void)
12169 InitElementPropertiesGfxElement();
12171 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12172 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12174 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12176 int element = getMappedElement(i);
12177 char basename1[16];
12178 char basename2[16];
12182 sprintf(basename1, "%04d.bmp", i);
12183 sprintf(basename2, "%04ds.bmp", i);
12185 filename1 = getPath2(global.create_images_dir, basename1);
12186 filename2 = getPath2(global.create_images_dir, basename2);
12188 DrawSizedElement(0, 0, element, TILESIZE);
12189 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12191 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12192 Fail("cannot save level sketch image file '%s'", filename1);
12194 DrawSizedElement(0, 0, element, MINI_TILESIZE);
12195 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12197 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12198 Fail("cannot save level sketch image file '%s'", filename2);
12203 // create corresponding SQL statements (for normal and small images)
12206 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12207 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12210 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12211 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12213 // optional: create content for forum level sketch demonstration post
12215 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12218 FreeBitmap(bitmap1);
12219 FreeBitmap(bitmap2);
12222 fprintf(stderr, "\n");
12224 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12226 CloseAllAndExit(0);
12230 // ----------------------------------------------------------------------------
12231 // create and save images for custom and group elements (raw BMP format)
12232 // ----------------------------------------------------------------------------
12234 void CreateCustomElementImages(char *directory)
12236 char *src_basename = "RocksCE-template.ilbm";
12237 char *dst_basename = "RocksCE.bmp";
12238 char *src_filename = getPath2(directory, src_basename);
12239 char *dst_filename = getPath2(directory, dst_basename);
12240 Bitmap *src_bitmap;
12242 int yoffset_ce = 0;
12243 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12246 InitVideoDefaults();
12248 ReCreateBitmap(&backbuffer, video.width, video.height);
12250 src_bitmap = LoadImage(src_filename);
12252 bitmap = CreateBitmap(TILEX * 16 * 2,
12253 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12256 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12263 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12264 TILEX * x, TILEY * y + yoffset_ce);
12266 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12268 TILEX * x + TILEX * 16,
12269 TILEY * y + yoffset_ce);
12271 for (j = 2; j >= 0; j--)
12275 BlitBitmap(src_bitmap, bitmap,
12276 TILEX + c * 7, 0, 6, 10,
12277 TILEX * x + 6 + j * 7,
12278 TILEY * y + 11 + yoffset_ce);
12280 BlitBitmap(src_bitmap, bitmap,
12281 TILEX + c * 8, TILEY, 6, 10,
12282 TILEX * 16 + TILEX * x + 6 + j * 8,
12283 TILEY * y + 10 + yoffset_ce);
12289 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12296 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12297 TILEX * x, TILEY * y + yoffset_ge);
12299 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12301 TILEX * x + TILEX * 16,
12302 TILEY * y + yoffset_ge);
12304 for (j = 1; j >= 0; j--)
12308 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12309 TILEX * x + 6 + j * 10,
12310 TILEY * y + 11 + yoffset_ge);
12312 BlitBitmap(src_bitmap, bitmap,
12313 TILEX + c * 8, TILEY + 12, 6, 10,
12314 TILEX * 16 + TILEX * x + 10 + j * 8,
12315 TILEY * y + 10 + yoffset_ge);
12321 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12322 Fail("cannot save CE graphics file '%s'", dst_filename);
12324 FreeBitmap(bitmap);
12326 CloseAllAndExit(0);