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
1080 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1081 &xx_ei.step_delay_fixed, 0,
1082 &yy_ei.step_delay_fixed
1086 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1087 &xx_ei.step_delay_random, 0,
1088 &yy_ei.step_delay_random
1093 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1094 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1099 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1100 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1101 &yy_ei.move_direction_initial
1105 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1106 &xx_ei.move_stepsize, TILEX / 8,
1107 &yy_ei.move_stepsize
1112 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1113 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1114 &yy_ei.move_enter_element
1118 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1119 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1120 &yy_ei.move_leave_element
1124 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1125 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1126 &yy_ei.move_leave_type
1131 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1132 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1133 &yy_ei.slippery_type
1138 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1139 &xx_ei.explosion_type, EXPLODES_3X3,
1140 &yy_ei.explosion_type
1144 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1145 &xx_ei.explosion_delay, 16,
1146 &yy_ei.explosion_delay
1150 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1151 &xx_ei.ignition_delay, 8,
1152 &yy_ei.ignition_delay
1157 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1158 &xx_ei.content, EL_EMPTY_SPACE,
1160 &xx_num_contents, 1, 1
1163 // ---------- "num_change_pages" must be the last entry ---------------------
1166 -1, SAVE_CONF_ALWAYS,
1167 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1168 &xx_ei.num_change_pages, 1,
1169 &yy_ei.num_change_pages
1180 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1182 // ---------- "current_change_page" must be the first entry -----------------
1185 -1, SAVE_CONF_ALWAYS,
1186 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1187 &xx_current_change_page, -1
1190 // ---------- (the remaining entries can be in any order) -------------------
1194 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1195 &xx_change.can_change, FALSE
1200 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1201 &xx_event_bits[0], 0
1205 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1206 &xx_event_bits[1], 0
1211 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1212 &xx_change.trigger_player, CH_PLAYER_ANY
1216 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1217 &xx_change.trigger_side, CH_SIDE_ANY
1221 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1222 &xx_change.trigger_page, CH_PAGE_ANY
1227 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1228 &xx_change.target_element, EL_EMPTY_SPACE
1233 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1234 &xx_change.delay_fixed, 0
1238 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1239 &xx_change.delay_random, 0
1243 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1244 &xx_change.delay_frames, FRAMES_PER_SECOND
1249 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1250 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1256 &xx_change.explode, FALSE
1260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1261 &xx_change.use_target_content, FALSE
1265 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1266 &xx_change.only_if_complete, FALSE
1270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1271 &xx_change.use_random_replace, FALSE
1275 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1276 &xx_change.random_percentage, 100
1280 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1281 &xx_change.replace_when, CP_WHEN_EMPTY
1286 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1287 &xx_change.has_action, FALSE
1291 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1292 &xx_change.action_type, CA_NO_ACTION
1296 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1297 &xx_change.action_mode, CA_MODE_UNDEFINED
1301 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1302 &xx_change.action_arg, CA_ARG_UNDEFINED
1307 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1308 &xx_change.action_element, EL_EMPTY_SPACE
1313 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1314 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1315 &xx_num_contents, 1, 1
1325 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1329 TYPE_STRING, CONF_VALUE_BYTES(1),
1330 &xx_ei.description[0], -1, NULL,
1331 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1332 &xx_default_description[0]
1337 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1338 &xx_ei.use_gfx_element, FALSE
1342 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1343 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1348 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1349 &xx_group.choice_mode, ANIM_RANDOM
1354 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1355 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1356 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1366 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1371 &li.block_snap_field, TRUE
1375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1376 &li.continuous_snapping, TRUE
1380 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1381 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1386 &li.use_start_element[0], FALSE
1390 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1391 &li.start_element[0], EL_PLAYER_1
1395 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1396 &li.use_artwork_element[0], FALSE
1400 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1401 &li.artwork_element[0], EL_PLAYER_1
1405 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1406 &li.use_explosion_element[0], FALSE
1410 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1411 &li.explosion_element[0], EL_PLAYER_1
1426 filetype_id_list[] =
1428 { LEVEL_FILE_TYPE_RND, "RND" },
1429 { LEVEL_FILE_TYPE_BD, "BD" },
1430 { LEVEL_FILE_TYPE_EM, "EM" },
1431 { LEVEL_FILE_TYPE_SP, "SP" },
1432 { LEVEL_FILE_TYPE_DX, "DX" },
1433 { LEVEL_FILE_TYPE_SB, "SB" },
1434 { LEVEL_FILE_TYPE_DC, "DC" },
1435 { LEVEL_FILE_TYPE_MM, "MM" },
1436 { LEVEL_FILE_TYPE_MM, "DF" },
1441 // ============================================================================
1442 // level file functions
1443 // ============================================================================
1445 static boolean check_special_flags(char *flag)
1447 if (strEqual(options.special_flags, flag) ||
1448 strEqual(leveldir_current->special_flags, flag))
1454 static struct DateInfo getCurrentDate(void)
1456 time_t epoch_seconds = time(NULL);
1457 struct tm *now = localtime(&epoch_seconds);
1458 struct DateInfo date;
1460 date.year = now->tm_year + 1900;
1461 date.month = now->tm_mon + 1;
1462 date.day = now->tm_mday;
1464 date.src = DATE_SRC_CLOCK;
1469 static void resetEventFlags(struct ElementChangeInfo *change)
1473 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1474 change->has_event[i] = FALSE;
1477 static void resetEventBits(void)
1481 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1482 xx_event_bits[i] = 0;
1485 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1489 /* important: only change event flag if corresponding event bit is set
1490 (this is because all xx_event_bits[] values are loaded separately,
1491 and all xx_event_bits[] values are set back to zero before loading
1492 another value xx_event_bits[x] (each value representing 32 flags)) */
1494 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1495 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1496 change->has_event[i] = TRUE;
1499 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1503 /* in contrast to the above function setEventFlagsFromEventBits(), it
1504 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1505 depending on the corresponding change->has_event[i] values here, as
1506 all xx_event_bits[] values are reset in resetEventBits() before */
1508 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1509 if (change->has_event[i])
1510 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1513 static char *getDefaultElementDescription(struct ElementInfo *ei)
1515 static char description[MAX_ELEMENT_NAME_LEN + 1];
1516 char *default_description = (ei->custom_description != NULL ?
1517 ei->custom_description :
1518 ei->editor_description);
1521 // always start with reliable default values
1522 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1523 description[i] = '\0';
1525 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1526 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1528 return &description[0];
1531 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1533 char *default_description = getDefaultElementDescription(ei);
1536 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1537 ei->description[i] = default_description[i];
1540 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1544 for (i = 0; conf[i].data_type != -1; i++)
1546 int default_value = conf[i].default_value;
1547 int data_type = conf[i].data_type;
1548 int conf_type = conf[i].conf_type;
1549 int byte_mask = conf_type & CONF_MASK_BYTES;
1551 if (byte_mask == CONF_MASK_MULTI_BYTES)
1553 int default_num_entities = conf[i].default_num_entities;
1554 int max_num_entities = conf[i].max_num_entities;
1556 *(int *)(conf[i].num_entities) = default_num_entities;
1558 if (data_type == TYPE_STRING)
1560 char *default_string = conf[i].default_string;
1561 char *string = (char *)(conf[i].value);
1563 strncpy(string, default_string, max_num_entities);
1565 else if (data_type == TYPE_ELEMENT_LIST)
1567 int *element_array = (int *)(conf[i].value);
1570 for (j = 0; j < max_num_entities; j++)
1571 element_array[j] = default_value;
1573 else if (data_type == TYPE_CONTENT_LIST)
1575 struct Content *content = (struct Content *)(conf[i].value);
1578 for (c = 0; c < max_num_entities; c++)
1579 for (y = 0; y < 3; y++)
1580 for (x = 0; x < 3; x++)
1581 content[c].e[x][y] = default_value;
1584 else // constant size configuration data (1, 2 or 4 bytes)
1586 if (data_type == TYPE_BOOLEAN)
1587 *(boolean *)(conf[i].value) = default_value;
1589 *(int *) (conf[i].value) = default_value;
1594 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1598 for (i = 0; conf[i].data_type != -1; i++)
1600 int data_type = conf[i].data_type;
1601 int conf_type = conf[i].conf_type;
1602 int byte_mask = conf_type & CONF_MASK_BYTES;
1604 if (byte_mask == CONF_MASK_MULTI_BYTES)
1606 int max_num_entities = conf[i].max_num_entities;
1608 if (data_type == TYPE_STRING)
1610 char *string = (char *)(conf[i].value);
1611 char *string_copy = (char *)(conf[i].value_copy);
1613 strncpy(string_copy, string, max_num_entities);
1615 else if (data_type == TYPE_ELEMENT_LIST)
1617 int *element_array = (int *)(conf[i].value);
1618 int *element_array_copy = (int *)(conf[i].value_copy);
1621 for (j = 0; j < max_num_entities; j++)
1622 element_array_copy[j] = element_array[j];
1624 else if (data_type == TYPE_CONTENT_LIST)
1626 struct Content *content = (struct Content *)(conf[i].value);
1627 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1630 for (c = 0; c < max_num_entities; c++)
1631 for (y = 0; y < 3; y++)
1632 for (x = 0; x < 3; x++)
1633 content_copy[c].e[x][y] = content[c].e[x][y];
1636 else // constant size configuration data (1, 2 or 4 bytes)
1638 if (data_type == TYPE_BOOLEAN)
1639 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1641 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1646 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1650 xx_ei = *ei_from; // copy element data into temporary buffer
1651 yy_ei = *ei_to; // copy element data into temporary buffer
1653 copyConfigFromConfigList(chunk_config_CUSX_base);
1658 // ---------- reinitialize and copy change pages ----------
1660 ei_to->num_change_pages = ei_from->num_change_pages;
1661 ei_to->current_change_page = ei_from->current_change_page;
1663 setElementChangePages(ei_to, ei_to->num_change_pages);
1665 for (i = 0; i < ei_to->num_change_pages; i++)
1666 ei_to->change_page[i] = ei_from->change_page[i];
1668 // ---------- copy group element info ----------
1669 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1670 *ei_to->group = *ei_from->group;
1672 // mark this custom element as modified
1673 ei_to->modified_settings = TRUE;
1676 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1678 int change_page_size = sizeof(struct ElementChangeInfo);
1680 ei->num_change_pages = MAX(1, change_pages);
1683 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1685 if (ei->current_change_page >= ei->num_change_pages)
1686 ei->current_change_page = ei->num_change_pages - 1;
1688 ei->change = &ei->change_page[ei->current_change_page];
1691 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1693 xx_change = *change; // copy change data into temporary buffer
1695 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1697 *change = xx_change;
1699 resetEventFlags(change);
1701 change->direct_action = 0;
1702 change->other_action = 0;
1704 change->pre_change_function = NULL;
1705 change->change_function = NULL;
1706 change->post_change_function = NULL;
1709 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1713 li = *level; // copy level data into temporary buffer
1714 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1715 *level = li; // copy temporary buffer back to level data
1717 setLevelInfoToDefaults_EM();
1718 setLevelInfoToDefaults_SP();
1719 setLevelInfoToDefaults_MM();
1721 level->native_em_level = &native_em_level;
1722 level->native_sp_level = &native_sp_level;
1723 level->native_mm_level = &native_mm_level;
1725 level->file_version = FILE_VERSION_ACTUAL;
1726 level->game_version = GAME_VERSION_ACTUAL;
1728 level->creation_date = getCurrentDate();
1730 level->encoding_16bit_field = TRUE;
1731 level->encoding_16bit_yamyam = TRUE;
1732 level->encoding_16bit_amoeba = TRUE;
1734 // clear level name and level author string buffers
1735 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1736 level->name[i] = '\0';
1737 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1738 level->author[i] = '\0';
1740 // set level name and level author to default values
1741 strcpy(level->name, NAMELESS_LEVEL_NAME);
1742 strcpy(level->author, ANONYMOUS_NAME);
1744 // set level playfield to playable default level with player and exit
1745 for (x = 0; x < MAX_LEV_FIELDX; x++)
1746 for (y = 0; y < MAX_LEV_FIELDY; y++)
1747 level->field[x][y] = EL_SAND;
1749 level->field[0][0] = EL_PLAYER_1;
1750 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1752 BorderElement = EL_STEELWALL;
1754 // detect custom elements when loading them
1755 level->file_has_custom_elements = FALSE;
1757 // set all bug compatibility flags to "false" => do not emulate this bug
1758 level->use_action_after_change_bug = FALSE;
1760 if (leveldir_current)
1762 // try to determine better author name than 'anonymous'
1763 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1765 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1766 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1770 switch (LEVELCLASS(leveldir_current))
1772 case LEVELCLASS_TUTORIAL:
1773 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1776 case LEVELCLASS_CONTRIB:
1777 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1778 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1781 case LEVELCLASS_PRIVATE:
1782 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1783 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1787 // keep default value
1794 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1796 static boolean clipboard_elements_initialized = FALSE;
1799 InitElementPropertiesStatic();
1801 li = *level; // copy level data into temporary buffer
1802 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1803 *level = li; // copy temporary buffer back to level data
1805 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1808 struct ElementInfo *ei = &element_info[element];
1810 // never initialize clipboard elements after the very first time
1811 // (to be able to use clipboard elements between several levels)
1812 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1815 if (IS_ENVELOPE(element))
1817 int envelope_nr = element - EL_ENVELOPE_1;
1819 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1821 level->envelope[envelope_nr] = xx_envelope;
1824 if (IS_CUSTOM_ELEMENT(element) ||
1825 IS_GROUP_ELEMENT(element) ||
1826 IS_INTERNAL_ELEMENT(element))
1828 xx_ei = *ei; // copy element data into temporary buffer
1830 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1835 setElementChangePages(ei, 1);
1836 setElementChangeInfoToDefaults(ei->change);
1838 if (IS_CUSTOM_ELEMENT(element) ||
1839 IS_GROUP_ELEMENT(element) ||
1840 IS_INTERNAL_ELEMENT(element))
1842 setElementDescriptionToDefault(ei);
1844 ei->modified_settings = FALSE;
1847 if (IS_CUSTOM_ELEMENT(element) ||
1848 IS_INTERNAL_ELEMENT(element))
1850 // internal values used in level editor
1852 ei->access_type = 0;
1853 ei->access_layer = 0;
1854 ei->access_protected = 0;
1855 ei->walk_to_action = 0;
1856 ei->smash_targets = 0;
1859 ei->can_explode_by_fire = FALSE;
1860 ei->can_explode_smashed = FALSE;
1861 ei->can_explode_impact = FALSE;
1863 ei->current_change_page = 0;
1866 if (IS_GROUP_ELEMENT(element) ||
1867 IS_INTERNAL_ELEMENT(element))
1869 struct ElementGroupInfo *group;
1871 // initialize memory for list of elements in group
1872 if (ei->group == NULL)
1873 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1877 xx_group = *group; // copy group data into temporary buffer
1879 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1885 clipboard_elements_initialized = TRUE;
1888 static void setLevelInfoToDefaults(struct LevelInfo *level,
1889 boolean level_info_only,
1890 boolean reset_file_status)
1892 setLevelInfoToDefaults_Level(level);
1894 if (!level_info_only)
1895 setLevelInfoToDefaults_Elements(level);
1897 if (reset_file_status)
1899 level->no_valid_file = FALSE;
1900 level->no_level_file = FALSE;
1903 level->changed = FALSE;
1906 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1908 level_file_info->nr = 0;
1909 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1910 level_file_info->packed = FALSE;
1912 setString(&level_file_info->basename, NULL);
1913 setString(&level_file_info->filename, NULL);
1916 int getMappedElement_SB(int, boolean);
1918 static void ActivateLevelTemplate(void)
1922 if (check_special_flags("load_xsb_to_ces"))
1924 // fill smaller playfields with padding "beyond border wall" elements
1925 if (level.fieldx < level_template.fieldx ||
1926 level.fieldy < level_template.fieldy)
1928 short field[level.fieldx][level.fieldy];
1929 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1930 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1931 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1932 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1934 // copy old playfield (which is smaller than the visible area)
1935 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1936 field[x][y] = level.field[x][y];
1938 // fill new, larger playfield with "beyond border wall" elements
1939 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1940 level.field[x][y] = getMappedElement_SB('_', TRUE);
1942 // copy the old playfield to the middle of the new playfield
1943 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1944 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1946 level.fieldx = new_fieldx;
1947 level.fieldy = new_fieldy;
1951 // Currently there is no special action needed to activate the template
1952 // data, because 'element_info' property settings overwrite the original
1953 // level data, while all other variables do not change.
1955 // Exception: 'from_level_template' elements in the original level playfield
1956 // are overwritten with the corresponding elements at the same position in
1957 // playfield from the level template.
1959 for (x = 0; x < level.fieldx; x++)
1960 for (y = 0; y < level.fieldy; y++)
1961 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1962 level.field[x][y] = level_template.field[x][y];
1964 if (check_special_flags("load_xsb_to_ces"))
1966 struct LevelInfo level_backup = level;
1968 // overwrite all individual level settings from template level settings
1969 level = level_template;
1971 // restore level file info
1972 level.file_info = level_backup.file_info;
1974 // restore playfield size
1975 level.fieldx = level_backup.fieldx;
1976 level.fieldy = level_backup.fieldy;
1978 // restore playfield content
1979 for (x = 0; x < level.fieldx; x++)
1980 for (y = 0; y < level.fieldy; y++)
1981 level.field[x][y] = level_backup.field[x][y];
1983 // restore name and author from individual level
1984 strcpy(level.name, level_backup.name);
1985 strcpy(level.author, level_backup.author);
1987 // restore flag "use_custom_template"
1988 level.use_custom_template = level_backup.use_custom_template;
1992 static char *getLevelFilenameFromBasename(char *basename)
1994 static char *filename = NULL;
1996 checked_free(filename);
1998 filename = getPath2(getCurrentLevelDir(), basename);
2003 static int getFileTypeFromBasename(char *basename)
2005 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2007 static char *filename = NULL;
2008 struct stat file_status;
2010 // ---------- try to determine file type from filename ----------
2012 // check for typical filename of a Supaplex level package file
2013 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2014 return LEVEL_FILE_TYPE_SP;
2016 // check for typical filename of a Diamond Caves II level package file
2017 if (strSuffixLower(basename, ".dc") ||
2018 strSuffixLower(basename, ".dc2"))
2019 return LEVEL_FILE_TYPE_DC;
2021 // check for typical filename of a Sokoban level package file
2022 if (strSuffixLower(basename, ".xsb") &&
2023 strchr(basename, '%') == NULL)
2024 return LEVEL_FILE_TYPE_SB;
2026 // ---------- try to determine file type from filesize ----------
2028 checked_free(filename);
2029 filename = getPath2(getCurrentLevelDir(), basename);
2031 if (stat(filename, &file_status) == 0)
2033 // check for typical filesize of a Supaplex level package file
2034 if (file_status.st_size == 170496)
2035 return LEVEL_FILE_TYPE_SP;
2038 return LEVEL_FILE_TYPE_UNKNOWN;
2041 static int getFileTypeFromMagicBytes(char *filename, int type)
2045 if ((file = openFile(filename, MODE_READ)))
2047 char chunk_name[CHUNK_ID_LEN + 1];
2049 getFileChunkBE(file, chunk_name, NULL);
2051 if (strEqual(chunk_name, "MMII") ||
2052 strEqual(chunk_name, "MIRR"))
2053 type = LEVEL_FILE_TYPE_MM;
2061 static boolean checkForPackageFromBasename(char *basename)
2063 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2064 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2066 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2069 static char *getSingleLevelBasenameExt(int nr, char *extension)
2071 static char basename[MAX_FILENAME_LEN];
2074 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2076 sprintf(basename, "%03d.%s", nr, extension);
2081 static char *getSingleLevelBasename(int nr)
2083 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2086 static char *getPackedLevelBasename(int type)
2088 static char basename[MAX_FILENAME_LEN];
2089 char *directory = getCurrentLevelDir();
2091 DirectoryEntry *dir_entry;
2093 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2095 if ((dir = openDirectory(directory)) == NULL)
2097 Warn("cannot read current level directory '%s'", directory);
2102 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2104 char *entry_basename = dir_entry->basename;
2105 int entry_type = getFileTypeFromBasename(entry_basename);
2107 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2109 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2112 strcpy(basename, entry_basename);
2119 closeDirectory(dir);
2124 static char *getSingleLevelFilename(int nr)
2126 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2129 #if ENABLE_UNUSED_CODE
2130 static char *getPackedLevelFilename(int type)
2132 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2136 char *getDefaultLevelFilename(int nr)
2138 return getSingleLevelFilename(nr);
2141 #if ENABLE_UNUSED_CODE
2142 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2146 lfi->packed = FALSE;
2148 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2149 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2153 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2154 int type, char *format, ...)
2156 static char basename[MAX_FILENAME_LEN];
2159 va_start(ap, format);
2160 vsprintf(basename, format, ap);
2164 lfi->packed = FALSE;
2166 setString(&lfi->basename, basename);
2167 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2170 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2176 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2177 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2180 static int getFiletypeFromID(char *filetype_id)
2182 char *filetype_id_lower;
2183 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2186 if (filetype_id == NULL)
2187 return LEVEL_FILE_TYPE_UNKNOWN;
2189 filetype_id_lower = getStringToLower(filetype_id);
2191 for (i = 0; filetype_id_list[i].id != NULL; i++)
2193 char *id_lower = getStringToLower(filetype_id_list[i].id);
2195 if (strEqual(filetype_id_lower, id_lower))
2196 filetype = filetype_id_list[i].filetype;
2200 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2204 free(filetype_id_lower);
2209 char *getLocalLevelTemplateFilename(void)
2211 return getDefaultLevelFilename(-1);
2214 char *getGlobalLevelTemplateFilename(void)
2216 // global variable "leveldir_current" must be modified in the loop below
2217 LevelDirTree *leveldir_current_last = leveldir_current;
2218 char *filename = NULL;
2220 // check for template level in path from current to topmost tree node
2222 while (leveldir_current != NULL)
2224 filename = getDefaultLevelFilename(-1);
2226 if (fileExists(filename))
2229 leveldir_current = leveldir_current->node_parent;
2232 // restore global variable "leveldir_current" modified in above loop
2233 leveldir_current = leveldir_current_last;
2238 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2242 // special case: level number is negative => check for level template file
2245 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2246 getSingleLevelBasename(-1));
2248 // replace local level template filename with global template filename
2249 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2251 // no fallback if template file not existing
2255 // special case: check for file name/pattern specified in "levelinfo.conf"
2256 if (leveldir_current->level_filename != NULL)
2258 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2260 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2261 leveldir_current->level_filename, nr);
2263 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2265 if (fileExists(lfi->filename))
2268 else if (leveldir_current->level_filetype != NULL)
2270 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2272 // check for specified native level file with standard file name
2273 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2274 "%03d.%s", nr, LEVELFILE_EXTENSION);
2275 if (fileExists(lfi->filename))
2279 // check for native Rocks'n'Diamonds level file
2280 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2281 "%03d.%s", nr, LEVELFILE_EXTENSION);
2282 if (fileExists(lfi->filename))
2285 // check for Emerald Mine level file (V1)
2286 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2287 'a' + (nr / 10) % 26, '0' + nr % 10);
2288 if (fileExists(lfi->filename))
2290 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2291 'A' + (nr / 10) % 26, '0' + nr % 10);
2292 if (fileExists(lfi->filename))
2295 // check for Emerald Mine level file (V2 to V5)
2296 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2297 if (fileExists(lfi->filename))
2300 // check for Emerald Mine level file (V6 / single mode)
2301 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2302 if (fileExists(lfi->filename))
2304 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2305 if (fileExists(lfi->filename))
2308 // check for Emerald Mine level file (V6 / teamwork mode)
2309 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2310 if (fileExists(lfi->filename))
2312 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2313 if (fileExists(lfi->filename))
2316 // check for various packed level file formats
2317 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2318 if (fileExists(lfi->filename))
2321 // no known level file found -- use default values (and fail later)
2322 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2323 "%03d.%s", nr, LEVELFILE_EXTENSION);
2326 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2328 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2329 lfi->type = getFileTypeFromBasename(lfi->basename);
2331 if (lfi->type == LEVEL_FILE_TYPE_RND)
2332 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2335 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2337 // always start with reliable default values
2338 setFileInfoToDefaults(level_file_info);
2340 level_file_info->nr = nr; // set requested level number
2342 determineLevelFileInfo_Filename(level_file_info);
2343 determineLevelFileInfo_Filetype(level_file_info);
2346 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2347 struct LevelFileInfo *lfi_to)
2349 lfi_to->nr = lfi_from->nr;
2350 lfi_to->type = lfi_from->type;
2351 lfi_to->packed = lfi_from->packed;
2353 setString(&lfi_to->basename, lfi_from->basename);
2354 setString(&lfi_to->filename, lfi_from->filename);
2357 // ----------------------------------------------------------------------------
2358 // functions for loading R'n'D level
2359 // ----------------------------------------------------------------------------
2361 int getMappedElement(int element)
2363 // remap some (historic, now obsolete) elements
2367 case EL_PLAYER_OBSOLETE:
2368 element = EL_PLAYER_1;
2371 case EL_KEY_OBSOLETE:
2375 case EL_EM_KEY_1_FILE_OBSOLETE:
2376 element = EL_EM_KEY_1;
2379 case EL_EM_KEY_2_FILE_OBSOLETE:
2380 element = EL_EM_KEY_2;
2383 case EL_EM_KEY_3_FILE_OBSOLETE:
2384 element = EL_EM_KEY_3;
2387 case EL_EM_KEY_4_FILE_OBSOLETE:
2388 element = EL_EM_KEY_4;
2391 case EL_ENVELOPE_OBSOLETE:
2392 element = EL_ENVELOPE_1;
2400 if (element >= NUM_FILE_ELEMENTS)
2402 Warn("invalid level element %d", element);
2404 element = EL_UNKNOWN;
2412 static int getMappedElementByVersion(int element, int game_version)
2414 // remap some elements due to certain game version
2416 if (game_version <= VERSION_IDENT(2,2,0,0))
2418 // map game font elements
2419 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2420 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2421 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2422 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2425 if (game_version < VERSION_IDENT(3,0,0,0))
2427 // map Supaplex gravity tube elements
2428 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2429 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2430 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2431 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2438 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2440 level->file_version = getFileVersion(file);
2441 level->game_version = getFileVersion(file);
2446 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2448 level->creation_date.year = getFile16BitBE(file);
2449 level->creation_date.month = getFile8Bit(file);
2450 level->creation_date.day = getFile8Bit(file);
2452 level->creation_date.src = DATE_SRC_LEVELFILE;
2457 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2459 int initial_player_stepsize;
2460 int initial_player_gravity;
2463 level->fieldx = getFile8Bit(file);
2464 level->fieldy = getFile8Bit(file);
2466 level->time = getFile16BitBE(file);
2467 level->gems_needed = getFile16BitBE(file);
2469 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2470 level->name[i] = getFile8Bit(file);
2471 level->name[MAX_LEVEL_NAME_LEN] = 0;
2473 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2474 level->score[i] = getFile8Bit(file);
2476 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2477 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2478 for (y = 0; y < 3; y++)
2479 for (x = 0; x < 3; x++)
2480 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2482 level->amoeba_speed = getFile8Bit(file);
2483 level->time_magic_wall = getFile8Bit(file);
2484 level->time_wheel = getFile8Bit(file);
2485 level->amoeba_content = getMappedElement(getFile8Bit(file));
2487 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2490 for (i = 0; i < MAX_PLAYERS; i++)
2491 level->initial_player_stepsize[i] = initial_player_stepsize;
2493 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2495 for (i = 0; i < MAX_PLAYERS; i++)
2496 level->initial_player_gravity[i] = initial_player_gravity;
2498 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2499 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2501 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2503 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2504 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2505 level->can_move_into_acid_bits = getFile32BitBE(file);
2506 level->dont_collide_with_bits = getFile8Bit(file);
2508 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2509 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2511 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2512 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2513 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2515 level->game_engine_type = getFile8Bit(file);
2517 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2522 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2526 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2527 level->name[i] = getFile8Bit(file);
2528 level->name[MAX_LEVEL_NAME_LEN] = 0;
2533 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2537 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2538 level->author[i] = getFile8Bit(file);
2539 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2544 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2547 int chunk_size_expected = level->fieldx * level->fieldy;
2549 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2550 stored with 16-bit encoding (and should be twice as big then).
2551 Even worse, playfield data was stored 16-bit when only yamyam content
2552 contained 16-bit elements and vice versa. */
2554 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2555 chunk_size_expected *= 2;
2557 if (chunk_size_expected != chunk_size)
2559 ReadUnusedBytesFromFile(file, chunk_size);
2560 return chunk_size_expected;
2563 for (y = 0; y < level->fieldy; y++)
2564 for (x = 0; x < level->fieldx; x++)
2565 level->field[x][y] =
2566 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2571 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2574 int header_size = 4;
2575 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2576 int chunk_size_expected = header_size + content_size;
2578 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2579 stored with 16-bit encoding (and should be twice as big then).
2580 Even worse, playfield data was stored 16-bit when only yamyam content
2581 contained 16-bit elements and vice versa. */
2583 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2584 chunk_size_expected += content_size;
2586 if (chunk_size_expected != chunk_size)
2588 ReadUnusedBytesFromFile(file, chunk_size);
2589 return chunk_size_expected;
2593 level->num_yamyam_contents = getFile8Bit(file);
2597 // correct invalid number of content fields -- should never happen
2598 if (level->num_yamyam_contents < 1 ||
2599 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2600 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2602 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2603 for (y = 0; y < 3; y++)
2604 for (x = 0; x < 3; x++)
2605 level->yamyam_content[i].e[x][y] =
2606 getMappedElement(level->encoding_16bit_field ?
2607 getFile16BitBE(file) : getFile8Bit(file));
2611 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2616 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2618 element = getMappedElement(getFile16BitBE(file));
2619 num_contents = getFile8Bit(file);
2621 getFile8Bit(file); // content x size (unused)
2622 getFile8Bit(file); // content y size (unused)
2624 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2626 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2627 for (y = 0; y < 3; y++)
2628 for (x = 0; x < 3; x++)
2629 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2631 // correct invalid number of content fields -- should never happen
2632 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2633 num_contents = STD_ELEMENT_CONTENTS;
2635 if (element == EL_YAMYAM)
2637 level->num_yamyam_contents = num_contents;
2639 for (i = 0; i < num_contents; i++)
2640 for (y = 0; y < 3; y++)
2641 for (x = 0; x < 3; x++)
2642 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2644 else if (element == EL_BD_AMOEBA)
2646 level->amoeba_content = content_array[0][0][0];
2650 Warn("cannot load content for element '%d'", element);
2656 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2662 int chunk_size_expected;
2664 element = getMappedElement(getFile16BitBE(file));
2665 if (!IS_ENVELOPE(element))
2666 element = EL_ENVELOPE_1;
2668 envelope_nr = element - EL_ENVELOPE_1;
2670 envelope_len = getFile16BitBE(file);
2672 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2673 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2675 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2677 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2678 if (chunk_size_expected != chunk_size)
2680 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2681 return chunk_size_expected;
2684 for (i = 0; i < envelope_len; i++)
2685 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2690 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2692 int num_changed_custom_elements = getFile16BitBE(file);
2693 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2696 if (chunk_size_expected != chunk_size)
2698 ReadUnusedBytesFromFile(file, chunk_size - 2);
2699 return chunk_size_expected;
2702 for (i = 0; i < num_changed_custom_elements; i++)
2704 int element = getMappedElement(getFile16BitBE(file));
2705 int properties = getFile32BitBE(file);
2707 if (IS_CUSTOM_ELEMENT(element))
2708 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2710 Warn("invalid custom element number %d", element);
2712 // older game versions that wrote level files with CUS1 chunks used
2713 // different default push delay values (not yet stored in level file)
2714 element_info[element].push_delay_fixed = 2;
2715 element_info[element].push_delay_random = 8;
2718 level->file_has_custom_elements = TRUE;
2723 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2725 int num_changed_custom_elements = getFile16BitBE(file);
2726 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2729 if (chunk_size_expected != chunk_size)
2731 ReadUnusedBytesFromFile(file, chunk_size - 2);
2732 return chunk_size_expected;
2735 for (i = 0; i < num_changed_custom_elements; i++)
2737 int element = getMappedElement(getFile16BitBE(file));
2738 int custom_target_element = getMappedElement(getFile16BitBE(file));
2740 if (IS_CUSTOM_ELEMENT(element))
2741 element_info[element].change->target_element = custom_target_element;
2743 Warn("invalid custom element number %d", element);
2746 level->file_has_custom_elements = TRUE;
2751 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2753 int num_changed_custom_elements = getFile16BitBE(file);
2754 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2757 if (chunk_size_expected != chunk_size)
2759 ReadUnusedBytesFromFile(file, chunk_size - 2);
2760 return chunk_size_expected;
2763 for (i = 0; i < num_changed_custom_elements; i++)
2765 int element = getMappedElement(getFile16BitBE(file));
2766 struct ElementInfo *ei = &element_info[element];
2767 unsigned int event_bits;
2769 if (!IS_CUSTOM_ELEMENT(element))
2771 Warn("invalid custom element number %d", element);
2773 element = EL_INTERNAL_DUMMY;
2776 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2777 ei->description[j] = getFile8Bit(file);
2778 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2780 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2782 // some free bytes for future properties and padding
2783 ReadUnusedBytesFromFile(file, 7);
2785 ei->use_gfx_element = getFile8Bit(file);
2786 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2788 ei->collect_score_initial = getFile8Bit(file);
2789 ei->collect_count_initial = getFile8Bit(file);
2791 ei->push_delay_fixed = getFile16BitBE(file);
2792 ei->push_delay_random = getFile16BitBE(file);
2793 ei->move_delay_fixed = getFile16BitBE(file);
2794 ei->move_delay_random = getFile16BitBE(file);
2796 ei->move_pattern = getFile16BitBE(file);
2797 ei->move_direction_initial = getFile8Bit(file);
2798 ei->move_stepsize = getFile8Bit(file);
2800 for (y = 0; y < 3; y++)
2801 for (x = 0; x < 3; x++)
2802 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2804 event_bits = getFile32BitBE(file);
2805 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2806 if (event_bits & (1 << j))
2807 ei->change->has_event[j] = TRUE;
2809 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2811 ei->change->delay_fixed = getFile16BitBE(file);
2812 ei->change->delay_random = getFile16BitBE(file);
2813 ei->change->delay_frames = getFile16BitBE(file);
2815 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2817 ei->change->explode = getFile8Bit(file);
2818 ei->change->use_target_content = getFile8Bit(file);
2819 ei->change->only_if_complete = getFile8Bit(file);
2820 ei->change->use_random_replace = getFile8Bit(file);
2822 ei->change->random_percentage = getFile8Bit(file);
2823 ei->change->replace_when = getFile8Bit(file);
2825 for (y = 0; y < 3; y++)
2826 for (x = 0; x < 3; x++)
2827 ei->change->target_content.e[x][y] =
2828 getMappedElement(getFile16BitBE(file));
2830 ei->slippery_type = getFile8Bit(file);
2832 // some free bytes for future properties and padding
2833 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2835 // mark that this custom element has been modified
2836 ei->modified_settings = TRUE;
2839 level->file_has_custom_elements = TRUE;
2844 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2846 struct ElementInfo *ei;
2847 int chunk_size_expected;
2851 // ---------- custom element base property values (96 bytes) ----------------
2853 element = getMappedElement(getFile16BitBE(file));
2855 if (!IS_CUSTOM_ELEMENT(element))
2857 Warn("invalid custom element number %d", element);
2859 ReadUnusedBytesFromFile(file, chunk_size - 2);
2864 ei = &element_info[element];
2866 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2867 ei->description[i] = getFile8Bit(file);
2868 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2870 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2872 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2874 ei->num_change_pages = getFile8Bit(file);
2876 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2877 if (chunk_size_expected != chunk_size)
2879 ReadUnusedBytesFromFile(file, chunk_size - 43);
2880 return chunk_size_expected;
2883 ei->ce_value_fixed_initial = getFile16BitBE(file);
2884 ei->ce_value_random_initial = getFile16BitBE(file);
2885 ei->use_last_ce_value = getFile8Bit(file);
2887 ei->use_gfx_element = getFile8Bit(file);
2888 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2890 ei->collect_score_initial = getFile8Bit(file);
2891 ei->collect_count_initial = getFile8Bit(file);
2893 ei->drop_delay_fixed = getFile8Bit(file);
2894 ei->push_delay_fixed = getFile8Bit(file);
2895 ei->drop_delay_random = getFile8Bit(file);
2896 ei->push_delay_random = getFile8Bit(file);
2897 ei->move_delay_fixed = getFile16BitBE(file);
2898 ei->move_delay_random = getFile16BitBE(file);
2900 // bits 0 - 15 of "move_pattern" ...
2901 ei->move_pattern = getFile16BitBE(file);
2902 ei->move_direction_initial = getFile8Bit(file);
2903 ei->move_stepsize = getFile8Bit(file);
2905 ei->slippery_type = getFile8Bit(file);
2907 for (y = 0; y < 3; y++)
2908 for (x = 0; x < 3; x++)
2909 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2911 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2912 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2913 ei->move_leave_type = getFile8Bit(file);
2915 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2916 ei->move_pattern |= (getFile16BitBE(file) << 16);
2918 ei->access_direction = getFile8Bit(file);
2920 ei->explosion_delay = getFile8Bit(file);
2921 ei->ignition_delay = getFile8Bit(file);
2922 ei->explosion_type = getFile8Bit(file);
2924 // some free bytes for future custom property values and padding
2925 ReadUnusedBytesFromFile(file, 1);
2927 // ---------- change page property values (48 bytes) ------------------------
2929 setElementChangePages(ei, ei->num_change_pages);
2931 for (i = 0; i < ei->num_change_pages; i++)
2933 struct ElementChangeInfo *change = &ei->change_page[i];
2934 unsigned int event_bits;
2936 // always start with reliable default values
2937 setElementChangeInfoToDefaults(change);
2939 // bits 0 - 31 of "has_event[]" ...
2940 event_bits = getFile32BitBE(file);
2941 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2942 if (event_bits & (1 << j))
2943 change->has_event[j] = TRUE;
2945 change->target_element = getMappedElement(getFile16BitBE(file));
2947 change->delay_fixed = getFile16BitBE(file);
2948 change->delay_random = getFile16BitBE(file);
2949 change->delay_frames = getFile16BitBE(file);
2951 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2953 change->explode = getFile8Bit(file);
2954 change->use_target_content = getFile8Bit(file);
2955 change->only_if_complete = getFile8Bit(file);
2956 change->use_random_replace = getFile8Bit(file);
2958 change->random_percentage = getFile8Bit(file);
2959 change->replace_when = getFile8Bit(file);
2961 for (y = 0; y < 3; y++)
2962 for (x = 0; x < 3; x++)
2963 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2965 change->can_change = getFile8Bit(file);
2967 change->trigger_side = getFile8Bit(file);
2969 change->trigger_player = getFile8Bit(file);
2970 change->trigger_page = getFile8Bit(file);
2972 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2973 CH_PAGE_ANY : (1 << change->trigger_page));
2975 change->has_action = getFile8Bit(file);
2976 change->action_type = getFile8Bit(file);
2977 change->action_mode = getFile8Bit(file);
2978 change->action_arg = getFile16BitBE(file);
2980 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2981 event_bits = getFile8Bit(file);
2982 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2983 if (event_bits & (1 << (j - 32)))
2984 change->has_event[j] = TRUE;
2987 // mark this custom element as modified
2988 ei->modified_settings = TRUE;
2990 level->file_has_custom_elements = TRUE;
2995 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2997 struct ElementInfo *ei;
2998 struct ElementGroupInfo *group;
3002 element = getMappedElement(getFile16BitBE(file));
3004 if (!IS_GROUP_ELEMENT(element))
3006 Warn("invalid group element number %d", element);
3008 ReadUnusedBytesFromFile(file, chunk_size - 2);
3013 ei = &element_info[element];
3015 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3016 ei->description[i] = getFile8Bit(file);
3017 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3019 group = element_info[element].group;
3021 group->num_elements = getFile8Bit(file);
3023 ei->use_gfx_element = getFile8Bit(file);
3024 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3026 group->choice_mode = getFile8Bit(file);
3028 // some free bytes for future values and padding
3029 ReadUnusedBytesFromFile(file, 3);
3031 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3032 group->element[i] = getMappedElement(getFile16BitBE(file));
3034 // mark this group element as modified
3035 element_info[element].modified_settings = TRUE;
3037 level->file_has_custom_elements = TRUE;
3042 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3043 int element, int real_element)
3045 int micro_chunk_size = 0;
3046 int conf_type = getFile8Bit(file);
3047 int byte_mask = conf_type & CONF_MASK_BYTES;
3048 boolean element_found = FALSE;
3051 micro_chunk_size += 1;
3053 if (byte_mask == CONF_MASK_MULTI_BYTES)
3055 int num_bytes = getFile16BitBE(file);
3056 byte *buffer = checked_malloc(num_bytes);
3058 ReadBytesFromFile(file, buffer, num_bytes);
3060 for (i = 0; conf[i].data_type != -1; i++)
3062 if (conf[i].element == element &&
3063 conf[i].conf_type == conf_type)
3065 int data_type = conf[i].data_type;
3066 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3067 int max_num_entities = conf[i].max_num_entities;
3069 if (num_entities > max_num_entities)
3071 Warn("truncating number of entities for element %d from %d to %d",
3072 element, num_entities, max_num_entities);
3074 num_entities = max_num_entities;
3077 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3078 data_type == TYPE_CONTENT_LIST))
3080 // for element and content lists, zero entities are not allowed
3081 Warn("found empty list of entities for element %d", element);
3083 // do not set "num_entities" here to prevent reading behind buffer
3085 *(int *)(conf[i].num_entities) = 1; // at least one is required
3089 *(int *)(conf[i].num_entities) = num_entities;
3092 element_found = TRUE;
3094 if (data_type == TYPE_STRING)
3096 char *string = (char *)(conf[i].value);
3099 for (j = 0; j < max_num_entities; j++)
3100 string[j] = (j < num_entities ? buffer[j] : '\0');
3102 else if (data_type == TYPE_ELEMENT_LIST)
3104 int *element_array = (int *)(conf[i].value);
3107 for (j = 0; j < num_entities; j++)
3109 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3111 else if (data_type == TYPE_CONTENT_LIST)
3113 struct Content *content= (struct Content *)(conf[i].value);
3116 for (c = 0; c < num_entities; c++)
3117 for (y = 0; y < 3; y++)
3118 for (x = 0; x < 3; x++)
3119 content[c].e[x][y] =
3120 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3123 element_found = FALSE;
3129 checked_free(buffer);
3131 micro_chunk_size += 2 + num_bytes;
3133 else // constant size configuration data (1, 2 or 4 bytes)
3135 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3136 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3137 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3139 for (i = 0; conf[i].data_type != -1; i++)
3141 if (conf[i].element == element &&
3142 conf[i].conf_type == conf_type)
3144 int data_type = conf[i].data_type;
3146 if (data_type == TYPE_ELEMENT)
3147 value = getMappedElement(value);
3149 if (data_type == TYPE_BOOLEAN)
3150 *(boolean *)(conf[i].value) = value;
3152 *(int *) (conf[i].value) = value;
3154 element_found = TRUE;
3160 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3165 char *error_conf_chunk_bytes =
3166 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3167 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3168 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3169 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3170 int error_element = real_element;
3172 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3173 error_conf_chunk_bytes, error_conf_chunk_token,
3174 error_element, EL_NAME(error_element));
3177 return micro_chunk_size;
3180 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3182 int real_chunk_size = 0;
3184 li = *level; // copy level data into temporary buffer
3186 while (!checkEndOfFile(file))
3188 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3190 if (real_chunk_size >= chunk_size)
3194 *level = li; // copy temporary buffer back to level data
3196 return real_chunk_size;
3199 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3201 int real_chunk_size = 0;
3203 li = *level; // copy level data into temporary buffer
3205 while (!checkEndOfFile(file))
3207 int element = getMappedElement(getFile16BitBE(file));
3209 real_chunk_size += 2;
3210 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3212 if (real_chunk_size >= chunk_size)
3216 *level = li; // copy temporary buffer back to level data
3218 return real_chunk_size;
3221 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3223 int real_chunk_size = 0;
3225 li = *level; // copy level data into temporary buffer
3227 while (!checkEndOfFile(file))
3229 int element = getMappedElement(getFile16BitBE(file));
3231 real_chunk_size += 2;
3232 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3234 if (real_chunk_size >= chunk_size)
3238 *level = li; // copy temporary buffer back to level data
3240 return real_chunk_size;
3243 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3245 int element = getMappedElement(getFile16BitBE(file));
3246 int envelope_nr = element - EL_ENVELOPE_1;
3247 int real_chunk_size = 2;
3249 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3251 while (!checkEndOfFile(file))
3253 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3256 if (real_chunk_size >= chunk_size)
3260 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3262 return real_chunk_size;
3265 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3267 int element = getMappedElement(getFile16BitBE(file));
3268 int real_chunk_size = 2;
3269 struct ElementInfo *ei = &element_info[element];
3272 xx_ei = *ei; // copy element data into temporary buffer
3274 xx_ei.num_change_pages = -1;
3276 while (!checkEndOfFile(file))
3278 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3280 if (xx_ei.num_change_pages != -1)
3283 if (real_chunk_size >= chunk_size)
3289 if (ei->num_change_pages == -1)
3291 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3294 ei->num_change_pages = 1;
3296 setElementChangePages(ei, 1);
3297 setElementChangeInfoToDefaults(ei->change);
3299 return real_chunk_size;
3302 // initialize number of change pages stored for this custom element
3303 setElementChangePages(ei, ei->num_change_pages);
3304 for (i = 0; i < ei->num_change_pages; i++)
3305 setElementChangeInfoToDefaults(&ei->change_page[i]);
3307 // start with reading properties for the first change page
3308 xx_current_change_page = 0;
3310 while (!checkEndOfFile(file))
3312 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3314 xx_change = *change; // copy change data into temporary buffer
3316 resetEventBits(); // reset bits; change page might have changed
3318 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3321 *change = xx_change;
3323 setEventFlagsFromEventBits(change);
3325 if (real_chunk_size >= chunk_size)
3329 level->file_has_custom_elements = TRUE;
3331 return real_chunk_size;
3334 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3336 int element = getMappedElement(getFile16BitBE(file));
3337 int real_chunk_size = 2;
3338 struct ElementInfo *ei = &element_info[element];
3339 struct ElementGroupInfo *group = ei->group;
3341 xx_ei = *ei; // copy element data into temporary buffer
3342 xx_group = *group; // copy group data into temporary buffer
3344 while (!checkEndOfFile(file))
3346 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3349 if (real_chunk_size >= chunk_size)
3356 level->file_has_custom_elements = TRUE;
3358 return real_chunk_size;
3361 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3362 struct LevelFileInfo *level_file_info,
3363 boolean level_info_only)
3365 char *filename = level_file_info->filename;
3366 char cookie[MAX_LINE_LEN];
3367 char chunk_name[CHUNK_ID_LEN + 1];
3371 if (!(file = openFile(filename, MODE_READ)))
3373 level->no_valid_file = TRUE;
3374 level->no_level_file = TRUE;
3376 if (level_info_only)
3379 Warn("cannot read level '%s' -- using empty level", filename);
3381 if (!setup.editor.use_template_for_new_levels)
3384 // if level file not found, try to initialize level data from template
3385 filename = getGlobalLevelTemplateFilename();
3387 if (!(file = openFile(filename, MODE_READ)))
3390 // default: for empty levels, use level template for custom elements
3391 level->use_custom_template = TRUE;
3393 level->no_valid_file = FALSE;
3396 getFileChunkBE(file, chunk_name, NULL);
3397 if (strEqual(chunk_name, "RND1"))
3399 getFile32BitBE(file); // not used
3401 getFileChunkBE(file, chunk_name, NULL);
3402 if (!strEqual(chunk_name, "CAVE"))
3404 level->no_valid_file = TRUE;
3406 Warn("unknown format of level file '%s'", filename);
3413 else // check for pre-2.0 file format with cookie string
3415 strcpy(cookie, chunk_name);
3416 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3418 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3419 cookie[strlen(cookie) - 1] = '\0';
3421 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3423 level->no_valid_file = TRUE;
3425 Warn("unknown format of level file '%s'", filename);
3432 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3434 level->no_valid_file = TRUE;
3436 Warn("unsupported version of level file '%s'", filename);
3443 // pre-2.0 level files have no game version, so use file version here
3444 level->game_version = level->file_version;
3447 if (level->file_version < FILE_VERSION_1_2)
3449 // level files from versions before 1.2.0 without chunk structure
3450 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3451 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3459 int (*loader)(File *, int, struct LevelInfo *);
3463 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3464 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3465 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3466 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3467 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3468 { "INFO", -1, LoadLevel_INFO },
3469 { "BODY", -1, LoadLevel_BODY },
3470 { "CONT", -1, LoadLevel_CONT },
3471 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3472 { "CNT3", -1, LoadLevel_CNT3 },
3473 { "CUS1", -1, LoadLevel_CUS1 },
3474 { "CUS2", -1, LoadLevel_CUS2 },
3475 { "CUS3", -1, LoadLevel_CUS3 },
3476 { "CUS4", -1, LoadLevel_CUS4 },
3477 { "GRP1", -1, LoadLevel_GRP1 },
3478 { "CONF", -1, LoadLevel_CONF },
3479 { "ELEM", -1, LoadLevel_ELEM },
3480 { "NOTE", -1, LoadLevel_NOTE },
3481 { "CUSX", -1, LoadLevel_CUSX },
3482 { "GRPX", -1, LoadLevel_GRPX },
3487 while (getFileChunkBE(file, chunk_name, &chunk_size))
3491 while (chunk_info[i].name != NULL &&
3492 !strEqual(chunk_name, chunk_info[i].name))
3495 if (chunk_info[i].name == NULL)
3497 Warn("unknown chunk '%s' in level file '%s'",
3498 chunk_name, filename);
3500 ReadUnusedBytesFromFile(file, chunk_size);
3502 else if (chunk_info[i].size != -1 &&
3503 chunk_info[i].size != chunk_size)
3505 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3506 chunk_size, chunk_name, filename);
3508 ReadUnusedBytesFromFile(file, chunk_size);
3512 // call function to load this level chunk
3513 int chunk_size_expected =
3514 (chunk_info[i].loader)(file, chunk_size, level);
3516 // the size of some chunks cannot be checked before reading other
3517 // chunks first (like "HEAD" and "BODY") that contain some header
3518 // information, so check them here
3519 if (chunk_size_expected != chunk_size)
3521 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3522 chunk_size, chunk_name, filename);
3532 // ----------------------------------------------------------------------------
3533 // functions for loading EM level
3534 // ----------------------------------------------------------------------------
3536 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3538 static int ball_xy[8][2] =
3549 struct LevelInfo_EM *level_em = level->native_em_level;
3550 struct CAVE *cav = level_em->cav;
3553 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3554 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3556 cav->time_seconds = level->time;
3557 cav->gems_needed = level->gems_needed;
3559 cav->emerald_score = level->score[SC_EMERALD];
3560 cav->diamond_score = level->score[SC_DIAMOND];
3561 cav->alien_score = level->score[SC_ROBOT];
3562 cav->tank_score = level->score[SC_SPACESHIP];
3563 cav->bug_score = level->score[SC_BUG];
3564 cav->eater_score = level->score[SC_YAMYAM];
3565 cav->nut_score = level->score[SC_NUT];
3566 cav->dynamite_score = level->score[SC_DYNAMITE];
3567 cav->key_score = level->score[SC_KEY];
3568 cav->exit_score = level->score[SC_TIME_BONUS];
3570 cav->num_eater_arrays = level->num_yamyam_contents;
3572 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3573 for (y = 0; y < 3; y++)
3574 for (x = 0; x < 3; x++)
3575 cav->eater_array[i][y * 3 + x] =
3576 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3578 cav->amoeba_time = level->amoeba_speed;
3579 cav->wonderwall_time = level->time_magic_wall;
3580 cav->wheel_time = level->time_wheel;
3582 cav->android_move_time = level->android_move_time;
3583 cav->android_clone_time = level->android_clone_time;
3584 cav->ball_random = level->ball_random;
3585 cav->ball_active = level->ball_active_initial;
3586 cav->ball_time = level->ball_time;
3587 cav->num_ball_arrays = level->num_ball_contents;
3589 cav->lenses_score = level->lenses_score;
3590 cav->magnify_score = level->magnify_score;
3591 cav->slurp_score = level->slurp_score;
3593 cav->lenses_time = level->lenses_time;
3594 cav->magnify_time = level->magnify_time;
3596 cav->wind_direction =
3597 map_direction_RND_to_EM(level->wind_direction_initial);
3599 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3600 for (j = 0; j < 8; j++)
3601 cav->ball_array[i][j] =
3602 map_element_RND_to_EM_cave(level->ball_content[i].
3603 e[ball_xy[j][0]][ball_xy[j][1]]);
3605 map_android_clone_elements_RND_to_EM(level);
3607 // first fill the complete playfield with the empty space element
3608 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3609 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3610 cav->cave[x][y] = Cblank;
3612 // then copy the real level contents from level file into the playfield
3613 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3615 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3617 if (level->field[x][y] == EL_AMOEBA_DEAD)
3618 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3620 cav->cave[x][y] = new_element;
3623 for (i = 0; i < MAX_PLAYERS; i++)
3625 cav->player_x[i] = -1;
3626 cav->player_y[i] = -1;
3629 // initialize player positions and delete players from the playfield
3630 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3632 if (ELEM_IS_PLAYER(level->field[x][y]))
3634 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3636 cav->player_x[player_nr] = x;
3637 cav->player_y[player_nr] = y;
3639 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3644 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3646 static int ball_xy[8][2] =
3657 struct LevelInfo_EM *level_em = level->native_em_level;
3658 struct CAVE *cav = level_em->cav;
3661 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3662 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3664 level->time = cav->time_seconds;
3665 level->gems_needed = cav->gems_needed;
3667 sprintf(level->name, "Level %d", level->file_info.nr);
3669 level->score[SC_EMERALD] = cav->emerald_score;
3670 level->score[SC_DIAMOND] = cav->diamond_score;
3671 level->score[SC_ROBOT] = cav->alien_score;
3672 level->score[SC_SPACESHIP] = cav->tank_score;
3673 level->score[SC_BUG] = cav->bug_score;
3674 level->score[SC_YAMYAM] = cav->eater_score;
3675 level->score[SC_NUT] = cav->nut_score;
3676 level->score[SC_DYNAMITE] = cav->dynamite_score;
3677 level->score[SC_KEY] = cav->key_score;
3678 level->score[SC_TIME_BONUS] = cav->exit_score;
3680 level->num_yamyam_contents = cav->num_eater_arrays;
3682 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3683 for (y = 0; y < 3; y++)
3684 for (x = 0; x < 3; x++)
3685 level->yamyam_content[i].e[x][y] =
3686 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3688 level->amoeba_speed = cav->amoeba_time;
3689 level->time_magic_wall = cav->wonderwall_time;
3690 level->time_wheel = cav->wheel_time;
3692 level->android_move_time = cav->android_move_time;
3693 level->android_clone_time = cav->android_clone_time;
3694 level->ball_random = cav->ball_random;
3695 level->ball_active_initial = cav->ball_active;
3696 level->ball_time = cav->ball_time;
3697 level->num_ball_contents = cav->num_ball_arrays;
3699 level->lenses_score = cav->lenses_score;
3700 level->magnify_score = cav->magnify_score;
3701 level->slurp_score = cav->slurp_score;
3703 level->lenses_time = cav->lenses_time;
3704 level->magnify_time = cav->magnify_time;
3706 level->wind_direction_initial =
3707 map_direction_EM_to_RND(cav->wind_direction);
3709 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3710 for (j = 0; j < 8; j++)
3711 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3712 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3714 map_android_clone_elements_EM_to_RND(level);
3716 // convert the playfield (some elements need special treatment)
3717 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3719 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3721 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3722 new_element = EL_AMOEBA_DEAD;
3724 level->field[x][y] = new_element;
3727 for (i = 0; i < MAX_PLAYERS; i++)
3729 // in case of all players set to the same field, use the first player
3730 int nr = MAX_PLAYERS - i - 1;
3731 int jx = cav->player_x[nr];
3732 int jy = cav->player_y[nr];
3734 if (jx != -1 && jy != -1)
3735 level->field[jx][jy] = EL_PLAYER_1 + nr;
3738 // time score is counted for each 10 seconds left in Emerald Mine levels
3739 level->time_score_base = 10;
3743 // ----------------------------------------------------------------------------
3744 // functions for loading SP level
3745 // ----------------------------------------------------------------------------
3747 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3749 struct LevelInfo_SP *level_sp = level->native_sp_level;
3750 LevelInfoType *header = &level_sp->header;
3753 level_sp->width = level->fieldx;
3754 level_sp->height = level->fieldy;
3756 for (x = 0; x < level->fieldx; x++)
3757 for (y = 0; y < level->fieldy; y++)
3758 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3760 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3762 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3763 header->LevelTitle[i] = level->name[i];
3764 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3766 header->InfotronsNeeded = level->gems_needed;
3768 header->SpecialPortCount = 0;
3770 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3772 boolean gravity_port_found = FALSE;
3773 boolean gravity_port_valid = FALSE;
3774 int gravity_port_flag;
3775 int gravity_port_base_element;
3776 int element = level->field[x][y];
3778 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3779 element <= EL_SP_GRAVITY_ON_PORT_UP)
3781 gravity_port_found = TRUE;
3782 gravity_port_valid = TRUE;
3783 gravity_port_flag = 1;
3784 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3786 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3787 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3789 gravity_port_found = TRUE;
3790 gravity_port_valid = TRUE;
3791 gravity_port_flag = 0;
3792 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3794 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3795 element <= EL_SP_GRAVITY_PORT_UP)
3797 // change R'n'D style gravity inverting special port to normal port
3798 // (there are no gravity inverting ports in native Supaplex engine)
3800 gravity_port_found = TRUE;
3801 gravity_port_valid = FALSE;
3802 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3805 if (gravity_port_found)
3807 if (gravity_port_valid &&
3808 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3810 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3812 port->PortLocation = (y * level->fieldx + x) * 2;
3813 port->Gravity = gravity_port_flag;
3815 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3817 header->SpecialPortCount++;
3821 // change special gravity port to normal port
3823 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3826 level_sp->playfield[x][y] = element - EL_SP_START;
3831 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3833 struct LevelInfo_SP *level_sp = level->native_sp_level;
3834 LevelInfoType *header = &level_sp->header;
3835 boolean num_invalid_elements = 0;
3838 level->fieldx = level_sp->width;
3839 level->fieldy = level_sp->height;
3841 for (x = 0; x < level->fieldx; x++)
3843 for (y = 0; y < level->fieldy; y++)
3845 int element_old = level_sp->playfield[x][y];
3846 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3848 if (element_new == EL_UNKNOWN)
3850 num_invalid_elements++;
3852 Debug("level:native:SP", "invalid element %d at position %d, %d",
3856 level->field[x][y] = element_new;
3860 if (num_invalid_elements > 0)
3861 Warn("found %d invalid elements%s", num_invalid_elements,
3862 (!options.debug ? " (use '--debug' for more details)" : ""));
3864 for (i = 0; i < MAX_PLAYERS; i++)
3865 level->initial_player_gravity[i] =
3866 (header->InitialGravity == 1 ? TRUE : FALSE);
3868 // skip leading spaces
3869 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3870 if (header->LevelTitle[i] != ' ')
3874 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3875 level->name[j] = header->LevelTitle[i];
3876 level->name[j] = '\0';
3878 // cut trailing spaces
3880 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3881 level->name[j - 1] = '\0';
3883 level->gems_needed = header->InfotronsNeeded;
3885 for (i = 0; i < header->SpecialPortCount; i++)
3887 SpecialPortType *port = &header->SpecialPort[i];
3888 int port_location = port->PortLocation;
3889 int gravity = port->Gravity;
3890 int port_x, port_y, port_element;
3892 port_x = (port_location / 2) % level->fieldx;
3893 port_y = (port_location / 2) / level->fieldx;
3895 if (port_x < 0 || port_x >= level->fieldx ||
3896 port_y < 0 || port_y >= level->fieldy)
3898 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3903 port_element = level->field[port_x][port_y];
3905 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3906 port_element > EL_SP_GRAVITY_PORT_UP)
3908 Warn("no special port at position (%d, %d)", port_x, port_y);
3913 // change previous (wrong) gravity inverting special port to either
3914 // gravity enabling special port or gravity disabling special port
3915 level->field[port_x][port_y] +=
3916 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3917 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3920 // change special gravity ports without database entries to normal ports
3921 for (x = 0; x < level->fieldx; x++)
3922 for (y = 0; y < level->fieldy; y++)
3923 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3924 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3925 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3927 level->time = 0; // no time limit
3928 level->amoeba_speed = 0;
3929 level->time_magic_wall = 0;
3930 level->time_wheel = 0;
3931 level->amoeba_content = EL_EMPTY;
3934 // original Supaplex does not use score values -- use default values
3936 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3937 level->score[i] = 0;
3940 // there are no yamyams in supaplex levels
3941 for (i = 0; i < level->num_yamyam_contents; i++)
3942 for (x = 0; x < 3; x++)
3943 for (y = 0; y < 3; y++)
3944 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3947 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3949 struct LevelInfo_SP *level_sp = level->native_sp_level;
3950 struct DemoInfo_SP *demo = &level_sp->demo;
3953 // always start with reliable default values
3954 demo->is_available = FALSE;
3957 if (TAPE_IS_EMPTY(tape))
3960 demo->level_nr = tape.level_nr; // (currently not used)
3962 level_sp->header.DemoRandomSeed = tape.random_seed;
3966 for (i = 0; i < tape.length; i++)
3968 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3969 int demo_repeat = tape.pos[i].delay;
3970 int demo_entries = (demo_repeat + 15) / 16;
3972 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3974 Warn("tape truncated: size exceeds maximum SP demo size %d",
3980 for (j = 0; j < demo_repeat / 16; j++)
3981 demo->data[demo->length++] = 0xf0 | demo_action;
3983 if (demo_repeat % 16)
3984 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3987 demo->is_available = TRUE;
3990 static void setTapeInfoToDefaults(void);
3992 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3994 struct LevelInfo_SP *level_sp = level->native_sp_level;
3995 struct DemoInfo_SP *demo = &level_sp->demo;
3996 char *filename = level->file_info.filename;
3999 // always start with reliable default values
4000 setTapeInfoToDefaults();
4002 if (!demo->is_available)
4005 tape.level_nr = demo->level_nr; // (currently not used)
4006 tape.random_seed = level_sp->header.DemoRandomSeed;
4008 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4011 tape.pos[tape.counter].delay = 0;
4013 for (i = 0; i < demo->length; i++)
4015 int demo_action = demo->data[i] & 0x0f;
4016 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4017 int tape_action = map_key_SP_to_RND(demo_action);
4018 int tape_repeat = demo_repeat + 1;
4019 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4020 boolean success = 0;
4023 for (j = 0; j < tape_repeat; j++)
4024 success = TapeAddAction(action);
4028 Warn("SP demo truncated: size exceeds maximum tape size %d",
4035 TapeHaltRecording();
4039 // ----------------------------------------------------------------------------
4040 // functions for loading MM level
4041 // ----------------------------------------------------------------------------
4043 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4045 struct LevelInfo_MM *level_mm = level->native_mm_level;
4048 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4049 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4051 level_mm->time = level->time;
4052 level_mm->kettles_needed = level->gems_needed;
4053 level_mm->auto_count_kettles = level->auto_count_gems;
4055 level_mm->laser_red = level->mm_laser_red;
4056 level_mm->laser_green = level->mm_laser_green;
4057 level_mm->laser_blue = level->mm_laser_blue;
4059 strcpy(level_mm->name, level->name);
4060 strcpy(level_mm->author, level->author);
4062 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4063 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4064 level_mm->score[SC_KEY] = level->score[SC_KEY];
4065 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4066 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4068 level_mm->amoeba_speed = level->amoeba_speed;
4069 level_mm->time_fuse = level->mm_time_fuse;
4070 level_mm->time_bomb = level->mm_time_bomb;
4071 level_mm->time_ball = level->mm_time_ball;
4072 level_mm->time_block = level->mm_time_block;
4074 for (x = 0; x < level->fieldx; x++)
4075 for (y = 0; y < level->fieldy; y++)
4077 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4080 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4082 struct LevelInfo_MM *level_mm = level->native_mm_level;
4085 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4086 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4088 level->time = level_mm->time;
4089 level->gems_needed = level_mm->kettles_needed;
4090 level->auto_count_gems = level_mm->auto_count_kettles;
4092 level->mm_laser_red = level_mm->laser_red;
4093 level->mm_laser_green = level_mm->laser_green;
4094 level->mm_laser_blue = level_mm->laser_blue;
4096 strcpy(level->name, level_mm->name);
4098 // only overwrite author from 'levelinfo.conf' if author defined in level
4099 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4100 strcpy(level->author, level_mm->author);
4102 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4103 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4104 level->score[SC_KEY] = level_mm->score[SC_KEY];
4105 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4106 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4108 level->amoeba_speed = level_mm->amoeba_speed;
4109 level->mm_time_fuse = level_mm->time_fuse;
4110 level->mm_time_bomb = level_mm->time_bomb;
4111 level->mm_time_ball = level_mm->time_ball;
4112 level->mm_time_block = level_mm->time_block;
4114 for (x = 0; x < level->fieldx; x++)
4115 for (y = 0; y < level->fieldy; y++)
4116 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4120 // ----------------------------------------------------------------------------
4121 // functions for loading DC level
4122 // ----------------------------------------------------------------------------
4124 #define DC_LEVEL_HEADER_SIZE 344
4126 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4129 static int last_data_encoded;
4133 int diff_hi, diff_lo;
4134 int data_hi, data_lo;
4135 unsigned short data_decoded;
4139 last_data_encoded = 0;
4146 diff = data_encoded - last_data_encoded;
4147 diff_hi = diff & ~0xff;
4148 diff_lo = diff & 0xff;
4152 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4153 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4154 data_hi = data_hi & 0xff00;
4156 data_decoded = data_hi | data_lo;
4158 last_data_encoded = data_encoded;
4160 offset1 = (offset1 + 1) % 31;
4161 offset2 = offset2 & 0xff;
4163 return data_decoded;
4166 static int getMappedElement_DC(int element)
4174 // 0x0117 - 0x036e: (?)
4177 // 0x042d - 0x0684: (?)
4193 element = EL_CRYSTAL;
4196 case 0x0e77: // quicksand (boulder)
4197 element = EL_QUICKSAND_FAST_FULL;
4200 case 0x0e99: // slow quicksand (boulder)
4201 element = EL_QUICKSAND_FULL;
4205 element = EL_EM_EXIT_OPEN;
4209 element = EL_EM_EXIT_CLOSED;
4213 element = EL_EM_STEEL_EXIT_OPEN;
4217 element = EL_EM_STEEL_EXIT_CLOSED;
4220 case 0x0f4f: // dynamite (lit 1)
4221 element = EL_EM_DYNAMITE_ACTIVE;
4224 case 0x0f57: // dynamite (lit 2)
4225 element = EL_EM_DYNAMITE_ACTIVE;
4228 case 0x0f5f: // dynamite (lit 3)
4229 element = EL_EM_DYNAMITE_ACTIVE;
4232 case 0x0f67: // dynamite (lit 4)
4233 element = EL_EM_DYNAMITE_ACTIVE;
4240 element = EL_AMOEBA_WET;
4244 element = EL_AMOEBA_DROP;
4248 element = EL_DC_MAGIC_WALL;
4252 element = EL_SPACESHIP_UP;
4256 element = EL_SPACESHIP_DOWN;
4260 element = EL_SPACESHIP_LEFT;
4264 element = EL_SPACESHIP_RIGHT;
4268 element = EL_BUG_UP;
4272 element = EL_BUG_DOWN;
4276 element = EL_BUG_LEFT;
4280 element = EL_BUG_RIGHT;
4284 element = EL_MOLE_UP;
4288 element = EL_MOLE_DOWN;
4292 element = EL_MOLE_LEFT;
4296 element = EL_MOLE_RIGHT;
4304 element = EL_YAMYAM_UP;
4308 element = EL_SWITCHGATE_OPEN;
4312 element = EL_SWITCHGATE_CLOSED;
4316 element = EL_DC_SWITCHGATE_SWITCH_UP;
4320 element = EL_TIMEGATE_CLOSED;
4323 case 0x144c: // conveyor belt switch (green)
4324 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4327 case 0x144f: // conveyor belt switch (red)
4328 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4331 case 0x1452: // conveyor belt switch (blue)
4332 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4336 element = EL_CONVEYOR_BELT_3_MIDDLE;
4340 element = EL_CONVEYOR_BELT_3_LEFT;
4344 element = EL_CONVEYOR_BELT_3_RIGHT;
4348 element = EL_CONVEYOR_BELT_1_MIDDLE;
4352 element = EL_CONVEYOR_BELT_1_LEFT;
4356 element = EL_CONVEYOR_BELT_1_RIGHT;
4360 element = EL_CONVEYOR_BELT_4_MIDDLE;
4364 element = EL_CONVEYOR_BELT_4_LEFT;
4368 element = EL_CONVEYOR_BELT_4_RIGHT;
4372 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4376 element = EL_EXPANDABLE_WALL_VERTICAL;
4380 element = EL_EXPANDABLE_WALL_ANY;
4383 case 0x14ce: // growing steel wall (left/right)
4384 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4387 case 0x14df: // growing steel wall (up/down)
4388 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4391 case 0x14e8: // growing steel wall (up/down/left/right)
4392 element = EL_EXPANDABLE_STEELWALL_ANY;
4396 element = EL_SHIELD_DEADLY;
4400 element = EL_EXTRA_TIME;
4408 element = EL_EMPTY_SPACE;
4411 case 0x1578: // quicksand (empty)
4412 element = EL_QUICKSAND_FAST_EMPTY;
4415 case 0x1579: // slow quicksand (empty)
4416 element = EL_QUICKSAND_EMPTY;
4426 element = EL_EM_DYNAMITE;
4429 case 0x15a1: // key (red)
4430 element = EL_EM_KEY_1;
4433 case 0x15a2: // key (yellow)
4434 element = EL_EM_KEY_2;
4437 case 0x15a3: // key (blue)
4438 element = EL_EM_KEY_4;
4441 case 0x15a4: // key (green)
4442 element = EL_EM_KEY_3;
4445 case 0x15a5: // key (white)
4446 element = EL_DC_KEY_WHITE;
4450 element = EL_WALL_SLIPPERY;
4457 case 0x15a8: // wall (not round)
4461 case 0x15a9: // (blue)
4462 element = EL_CHAR_A;
4465 case 0x15aa: // (blue)
4466 element = EL_CHAR_B;
4469 case 0x15ab: // (blue)
4470 element = EL_CHAR_C;
4473 case 0x15ac: // (blue)
4474 element = EL_CHAR_D;
4477 case 0x15ad: // (blue)
4478 element = EL_CHAR_E;
4481 case 0x15ae: // (blue)
4482 element = EL_CHAR_F;
4485 case 0x15af: // (blue)
4486 element = EL_CHAR_G;
4489 case 0x15b0: // (blue)
4490 element = EL_CHAR_H;
4493 case 0x15b1: // (blue)
4494 element = EL_CHAR_I;
4497 case 0x15b2: // (blue)
4498 element = EL_CHAR_J;
4501 case 0x15b3: // (blue)
4502 element = EL_CHAR_K;
4505 case 0x15b4: // (blue)
4506 element = EL_CHAR_L;
4509 case 0x15b5: // (blue)
4510 element = EL_CHAR_M;
4513 case 0x15b6: // (blue)
4514 element = EL_CHAR_N;
4517 case 0x15b7: // (blue)
4518 element = EL_CHAR_O;
4521 case 0x15b8: // (blue)
4522 element = EL_CHAR_P;
4525 case 0x15b9: // (blue)
4526 element = EL_CHAR_Q;
4529 case 0x15ba: // (blue)
4530 element = EL_CHAR_R;
4533 case 0x15bb: // (blue)
4534 element = EL_CHAR_S;
4537 case 0x15bc: // (blue)
4538 element = EL_CHAR_T;
4541 case 0x15bd: // (blue)
4542 element = EL_CHAR_U;
4545 case 0x15be: // (blue)
4546 element = EL_CHAR_V;
4549 case 0x15bf: // (blue)
4550 element = EL_CHAR_W;
4553 case 0x15c0: // (blue)
4554 element = EL_CHAR_X;
4557 case 0x15c1: // (blue)
4558 element = EL_CHAR_Y;
4561 case 0x15c2: // (blue)
4562 element = EL_CHAR_Z;
4565 case 0x15c3: // (blue)
4566 element = EL_CHAR_AUMLAUT;
4569 case 0x15c4: // (blue)
4570 element = EL_CHAR_OUMLAUT;
4573 case 0x15c5: // (blue)
4574 element = EL_CHAR_UUMLAUT;
4577 case 0x15c6: // (blue)
4578 element = EL_CHAR_0;
4581 case 0x15c7: // (blue)
4582 element = EL_CHAR_1;
4585 case 0x15c8: // (blue)
4586 element = EL_CHAR_2;
4589 case 0x15c9: // (blue)
4590 element = EL_CHAR_3;
4593 case 0x15ca: // (blue)
4594 element = EL_CHAR_4;
4597 case 0x15cb: // (blue)
4598 element = EL_CHAR_5;
4601 case 0x15cc: // (blue)
4602 element = EL_CHAR_6;
4605 case 0x15cd: // (blue)
4606 element = EL_CHAR_7;
4609 case 0x15ce: // (blue)
4610 element = EL_CHAR_8;
4613 case 0x15cf: // (blue)
4614 element = EL_CHAR_9;
4617 case 0x15d0: // (blue)
4618 element = EL_CHAR_PERIOD;
4621 case 0x15d1: // (blue)
4622 element = EL_CHAR_EXCLAM;
4625 case 0x15d2: // (blue)
4626 element = EL_CHAR_COLON;
4629 case 0x15d3: // (blue)
4630 element = EL_CHAR_LESS;
4633 case 0x15d4: // (blue)
4634 element = EL_CHAR_GREATER;
4637 case 0x15d5: // (blue)
4638 element = EL_CHAR_QUESTION;
4641 case 0x15d6: // (blue)
4642 element = EL_CHAR_COPYRIGHT;
4645 case 0x15d7: // (blue)
4646 element = EL_CHAR_UP;
4649 case 0x15d8: // (blue)
4650 element = EL_CHAR_DOWN;
4653 case 0x15d9: // (blue)
4654 element = EL_CHAR_BUTTON;
4657 case 0x15da: // (blue)
4658 element = EL_CHAR_PLUS;
4661 case 0x15db: // (blue)
4662 element = EL_CHAR_MINUS;
4665 case 0x15dc: // (blue)
4666 element = EL_CHAR_APOSTROPHE;
4669 case 0x15dd: // (blue)
4670 element = EL_CHAR_PARENLEFT;
4673 case 0x15de: // (blue)
4674 element = EL_CHAR_PARENRIGHT;
4677 case 0x15df: // (green)
4678 element = EL_CHAR_A;
4681 case 0x15e0: // (green)
4682 element = EL_CHAR_B;
4685 case 0x15e1: // (green)
4686 element = EL_CHAR_C;
4689 case 0x15e2: // (green)
4690 element = EL_CHAR_D;
4693 case 0x15e3: // (green)
4694 element = EL_CHAR_E;
4697 case 0x15e4: // (green)
4698 element = EL_CHAR_F;
4701 case 0x15e5: // (green)
4702 element = EL_CHAR_G;
4705 case 0x15e6: // (green)
4706 element = EL_CHAR_H;
4709 case 0x15e7: // (green)
4710 element = EL_CHAR_I;
4713 case 0x15e8: // (green)
4714 element = EL_CHAR_J;
4717 case 0x15e9: // (green)
4718 element = EL_CHAR_K;
4721 case 0x15ea: // (green)
4722 element = EL_CHAR_L;
4725 case 0x15eb: // (green)
4726 element = EL_CHAR_M;
4729 case 0x15ec: // (green)
4730 element = EL_CHAR_N;
4733 case 0x15ed: // (green)
4734 element = EL_CHAR_O;
4737 case 0x15ee: // (green)
4738 element = EL_CHAR_P;
4741 case 0x15ef: // (green)
4742 element = EL_CHAR_Q;
4745 case 0x15f0: // (green)
4746 element = EL_CHAR_R;
4749 case 0x15f1: // (green)
4750 element = EL_CHAR_S;
4753 case 0x15f2: // (green)
4754 element = EL_CHAR_T;
4757 case 0x15f3: // (green)
4758 element = EL_CHAR_U;
4761 case 0x15f4: // (green)
4762 element = EL_CHAR_V;
4765 case 0x15f5: // (green)
4766 element = EL_CHAR_W;
4769 case 0x15f6: // (green)
4770 element = EL_CHAR_X;
4773 case 0x15f7: // (green)
4774 element = EL_CHAR_Y;
4777 case 0x15f8: // (green)
4778 element = EL_CHAR_Z;
4781 case 0x15f9: // (green)
4782 element = EL_CHAR_AUMLAUT;
4785 case 0x15fa: // (green)
4786 element = EL_CHAR_OUMLAUT;
4789 case 0x15fb: // (green)
4790 element = EL_CHAR_UUMLAUT;
4793 case 0x15fc: // (green)
4794 element = EL_CHAR_0;
4797 case 0x15fd: // (green)
4798 element = EL_CHAR_1;
4801 case 0x15fe: // (green)
4802 element = EL_CHAR_2;
4805 case 0x15ff: // (green)
4806 element = EL_CHAR_3;
4809 case 0x1600: // (green)
4810 element = EL_CHAR_4;
4813 case 0x1601: // (green)
4814 element = EL_CHAR_5;
4817 case 0x1602: // (green)
4818 element = EL_CHAR_6;
4821 case 0x1603: // (green)
4822 element = EL_CHAR_7;
4825 case 0x1604: // (green)
4826 element = EL_CHAR_8;
4829 case 0x1605: // (green)
4830 element = EL_CHAR_9;
4833 case 0x1606: // (green)
4834 element = EL_CHAR_PERIOD;
4837 case 0x1607: // (green)
4838 element = EL_CHAR_EXCLAM;
4841 case 0x1608: // (green)
4842 element = EL_CHAR_COLON;
4845 case 0x1609: // (green)
4846 element = EL_CHAR_LESS;
4849 case 0x160a: // (green)
4850 element = EL_CHAR_GREATER;
4853 case 0x160b: // (green)
4854 element = EL_CHAR_QUESTION;
4857 case 0x160c: // (green)
4858 element = EL_CHAR_COPYRIGHT;
4861 case 0x160d: // (green)
4862 element = EL_CHAR_UP;
4865 case 0x160e: // (green)
4866 element = EL_CHAR_DOWN;
4869 case 0x160f: // (green)
4870 element = EL_CHAR_BUTTON;
4873 case 0x1610: // (green)
4874 element = EL_CHAR_PLUS;
4877 case 0x1611: // (green)
4878 element = EL_CHAR_MINUS;
4881 case 0x1612: // (green)
4882 element = EL_CHAR_APOSTROPHE;
4885 case 0x1613: // (green)
4886 element = EL_CHAR_PARENLEFT;
4889 case 0x1614: // (green)
4890 element = EL_CHAR_PARENRIGHT;
4893 case 0x1615: // (blue steel)
4894 element = EL_STEEL_CHAR_A;
4897 case 0x1616: // (blue steel)
4898 element = EL_STEEL_CHAR_B;
4901 case 0x1617: // (blue steel)
4902 element = EL_STEEL_CHAR_C;
4905 case 0x1618: // (blue steel)
4906 element = EL_STEEL_CHAR_D;
4909 case 0x1619: // (blue steel)
4910 element = EL_STEEL_CHAR_E;
4913 case 0x161a: // (blue steel)
4914 element = EL_STEEL_CHAR_F;
4917 case 0x161b: // (blue steel)
4918 element = EL_STEEL_CHAR_G;
4921 case 0x161c: // (blue steel)
4922 element = EL_STEEL_CHAR_H;
4925 case 0x161d: // (blue steel)
4926 element = EL_STEEL_CHAR_I;
4929 case 0x161e: // (blue steel)
4930 element = EL_STEEL_CHAR_J;
4933 case 0x161f: // (blue steel)
4934 element = EL_STEEL_CHAR_K;
4937 case 0x1620: // (blue steel)
4938 element = EL_STEEL_CHAR_L;
4941 case 0x1621: // (blue steel)
4942 element = EL_STEEL_CHAR_M;
4945 case 0x1622: // (blue steel)
4946 element = EL_STEEL_CHAR_N;
4949 case 0x1623: // (blue steel)
4950 element = EL_STEEL_CHAR_O;
4953 case 0x1624: // (blue steel)
4954 element = EL_STEEL_CHAR_P;
4957 case 0x1625: // (blue steel)
4958 element = EL_STEEL_CHAR_Q;
4961 case 0x1626: // (blue steel)
4962 element = EL_STEEL_CHAR_R;
4965 case 0x1627: // (blue steel)
4966 element = EL_STEEL_CHAR_S;
4969 case 0x1628: // (blue steel)
4970 element = EL_STEEL_CHAR_T;
4973 case 0x1629: // (blue steel)
4974 element = EL_STEEL_CHAR_U;
4977 case 0x162a: // (blue steel)
4978 element = EL_STEEL_CHAR_V;
4981 case 0x162b: // (blue steel)
4982 element = EL_STEEL_CHAR_W;
4985 case 0x162c: // (blue steel)
4986 element = EL_STEEL_CHAR_X;
4989 case 0x162d: // (blue steel)
4990 element = EL_STEEL_CHAR_Y;
4993 case 0x162e: // (blue steel)
4994 element = EL_STEEL_CHAR_Z;
4997 case 0x162f: // (blue steel)
4998 element = EL_STEEL_CHAR_AUMLAUT;
5001 case 0x1630: // (blue steel)
5002 element = EL_STEEL_CHAR_OUMLAUT;
5005 case 0x1631: // (blue steel)
5006 element = EL_STEEL_CHAR_UUMLAUT;
5009 case 0x1632: // (blue steel)
5010 element = EL_STEEL_CHAR_0;
5013 case 0x1633: // (blue steel)
5014 element = EL_STEEL_CHAR_1;
5017 case 0x1634: // (blue steel)
5018 element = EL_STEEL_CHAR_2;
5021 case 0x1635: // (blue steel)
5022 element = EL_STEEL_CHAR_3;
5025 case 0x1636: // (blue steel)
5026 element = EL_STEEL_CHAR_4;
5029 case 0x1637: // (blue steel)
5030 element = EL_STEEL_CHAR_5;
5033 case 0x1638: // (blue steel)
5034 element = EL_STEEL_CHAR_6;
5037 case 0x1639: // (blue steel)
5038 element = EL_STEEL_CHAR_7;
5041 case 0x163a: // (blue steel)
5042 element = EL_STEEL_CHAR_8;
5045 case 0x163b: // (blue steel)
5046 element = EL_STEEL_CHAR_9;
5049 case 0x163c: // (blue steel)
5050 element = EL_STEEL_CHAR_PERIOD;
5053 case 0x163d: // (blue steel)
5054 element = EL_STEEL_CHAR_EXCLAM;
5057 case 0x163e: // (blue steel)
5058 element = EL_STEEL_CHAR_COLON;
5061 case 0x163f: // (blue steel)
5062 element = EL_STEEL_CHAR_LESS;
5065 case 0x1640: // (blue steel)
5066 element = EL_STEEL_CHAR_GREATER;
5069 case 0x1641: // (blue steel)
5070 element = EL_STEEL_CHAR_QUESTION;
5073 case 0x1642: // (blue steel)
5074 element = EL_STEEL_CHAR_COPYRIGHT;
5077 case 0x1643: // (blue steel)
5078 element = EL_STEEL_CHAR_UP;
5081 case 0x1644: // (blue steel)
5082 element = EL_STEEL_CHAR_DOWN;
5085 case 0x1645: // (blue steel)
5086 element = EL_STEEL_CHAR_BUTTON;
5089 case 0x1646: // (blue steel)
5090 element = EL_STEEL_CHAR_PLUS;
5093 case 0x1647: // (blue steel)
5094 element = EL_STEEL_CHAR_MINUS;
5097 case 0x1648: // (blue steel)
5098 element = EL_STEEL_CHAR_APOSTROPHE;
5101 case 0x1649: // (blue steel)
5102 element = EL_STEEL_CHAR_PARENLEFT;
5105 case 0x164a: // (blue steel)
5106 element = EL_STEEL_CHAR_PARENRIGHT;
5109 case 0x164b: // (green steel)
5110 element = EL_STEEL_CHAR_A;
5113 case 0x164c: // (green steel)
5114 element = EL_STEEL_CHAR_B;
5117 case 0x164d: // (green steel)
5118 element = EL_STEEL_CHAR_C;
5121 case 0x164e: // (green steel)
5122 element = EL_STEEL_CHAR_D;
5125 case 0x164f: // (green steel)
5126 element = EL_STEEL_CHAR_E;
5129 case 0x1650: // (green steel)
5130 element = EL_STEEL_CHAR_F;
5133 case 0x1651: // (green steel)
5134 element = EL_STEEL_CHAR_G;
5137 case 0x1652: // (green steel)
5138 element = EL_STEEL_CHAR_H;
5141 case 0x1653: // (green steel)
5142 element = EL_STEEL_CHAR_I;
5145 case 0x1654: // (green steel)
5146 element = EL_STEEL_CHAR_J;
5149 case 0x1655: // (green steel)
5150 element = EL_STEEL_CHAR_K;
5153 case 0x1656: // (green steel)
5154 element = EL_STEEL_CHAR_L;
5157 case 0x1657: // (green steel)
5158 element = EL_STEEL_CHAR_M;
5161 case 0x1658: // (green steel)
5162 element = EL_STEEL_CHAR_N;
5165 case 0x1659: // (green steel)
5166 element = EL_STEEL_CHAR_O;
5169 case 0x165a: // (green steel)
5170 element = EL_STEEL_CHAR_P;
5173 case 0x165b: // (green steel)
5174 element = EL_STEEL_CHAR_Q;
5177 case 0x165c: // (green steel)
5178 element = EL_STEEL_CHAR_R;
5181 case 0x165d: // (green steel)
5182 element = EL_STEEL_CHAR_S;
5185 case 0x165e: // (green steel)
5186 element = EL_STEEL_CHAR_T;
5189 case 0x165f: // (green steel)
5190 element = EL_STEEL_CHAR_U;
5193 case 0x1660: // (green steel)
5194 element = EL_STEEL_CHAR_V;
5197 case 0x1661: // (green steel)
5198 element = EL_STEEL_CHAR_W;
5201 case 0x1662: // (green steel)
5202 element = EL_STEEL_CHAR_X;
5205 case 0x1663: // (green steel)
5206 element = EL_STEEL_CHAR_Y;
5209 case 0x1664: // (green steel)
5210 element = EL_STEEL_CHAR_Z;
5213 case 0x1665: // (green steel)
5214 element = EL_STEEL_CHAR_AUMLAUT;
5217 case 0x1666: // (green steel)
5218 element = EL_STEEL_CHAR_OUMLAUT;
5221 case 0x1667: // (green steel)
5222 element = EL_STEEL_CHAR_UUMLAUT;
5225 case 0x1668: // (green steel)
5226 element = EL_STEEL_CHAR_0;
5229 case 0x1669: // (green steel)
5230 element = EL_STEEL_CHAR_1;
5233 case 0x166a: // (green steel)
5234 element = EL_STEEL_CHAR_2;
5237 case 0x166b: // (green steel)
5238 element = EL_STEEL_CHAR_3;
5241 case 0x166c: // (green steel)
5242 element = EL_STEEL_CHAR_4;
5245 case 0x166d: // (green steel)
5246 element = EL_STEEL_CHAR_5;
5249 case 0x166e: // (green steel)
5250 element = EL_STEEL_CHAR_6;
5253 case 0x166f: // (green steel)
5254 element = EL_STEEL_CHAR_7;
5257 case 0x1670: // (green steel)
5258 element = EL_STEEL_CHAR_8;
5261 case 0x1671: // (green steel)
5262 element = EL_STEEL_CHAR_9;
5265 case 0x1672: // (green steel)
5266 element = EL_STEEL_CHAR_PERIOD;
5269 case 0x1673: // (green steel)
5270 element = EL_STEEL_CHAR_EXCLAM;
5273 case 0x1674: // (green steel)
5274 element = EL_STEEL_CHAR_COLON;
5277 case 0x1675: // (green steel)
5278 element = EL_STEEL_CHAR_LESS;
5281 case 0x1676: // (green steel)
5282 element = EL_STEEL_CHAR_GREATER;
5285 case 0x1677: // (green steel)
5286 element = EL_STEEL_CHAR_QUESTION;
5289 case 0x1678: // (green steel)
5290 element = EL_STEEL_CHAR_COPYRIGHT;
5293 case 0x1679: // (green steel)
5294 element = EL_STEEL_CHAR_UP;
5297 case 0x167a: // (green steel)
5298 element = EL_STEEL_CHAR_DOWN;
5301 case 0x167b: // (green steel)
5302 element = EL_STEEL_CHAR_BUTTON;
5305 case 0x167c: // (green steel)
5306 element = EL_STEEL_CHAR_PLUS;
5309 case 0x167d: // (green steel)
5310 element = EL_STEEL_CHAR_MINUS;
5313 case 0x167e: // (green steel)
5314 element = EL_STEEL_CHAR_APOSTROPHE;
5317 case 0x167f: // (green steel)
5318 element = EL_STEEL_CHAR_PARENLEFT;
5321 case 0x1680: // (green steel)
5322 element = EL_STEEL_CHAR_PARENRIGHT;
5325 case 0x1681: // gate (red)
5326 element = EL_EM_GATE_1;
5329 case 0x1682: // secret gate (red)
5330 element = EL_EM_GATE_1_GRAY;
5333 case 0x1683: // gate (yellow)
5334 element = EL_EM_GATE_2;
5337 case 0x1684: // secret gate (yellow)
5338 element = EL_EM_GATE_2_GRAY;
5341 case 0x1685: // gate (blue)
5342 element = EL_EM_GATE_4;
5345 case 0x1686: // secret gate (blue)
5346 element = EL_EM_GATE_4_GRAY;
5349 case 0x1687: // gate (green)
5350 element = EL_EM_GATE_3;
5353 case 0x1688: // secret gate (green)
5354 element = EL_EM_GATE_3_GRAY;
5357 case 0x1689: // gate (white)
5358 element = EL_DC_GATE_WHITE;
5361 case 0x168a: // secret gate (white)
5362 element = EL_DC_GATE_WHITE_GRAY;
5365 case 0x168b: // secret gate (no key)
5366 element = EL_DC_GATE_FAKE_GRAY;
5370 element = EL_ROBOT_WHEEL;
5374 element = EL_DC_TIMEGATE_SWITCH;
5378 element = EL_ACID_POOL_BOTTOM;
5382 element = EL_ACID_POOL_TOPLEFT;
5386 element = EL_ACID_POOL_TOPRIGHT;
5390 element = EL_ACID_POOL_BOTTOMLEFT;
5394 element = EL_ACID_POOL_BOTTOMRIGHT;
5398 element = EL_STEELWALL;
5402 element = EL_STEELWALL_SLIPPERY;
5405 case 0x1695: // steel wall (not round)
5406 element = EL_STEELWALL;
5409 case 0x1696: // steel wall (left)
5410 element = EL_DC_STEELWALL_1_LEFT;
5413 case 0x1697: // steel wall (bottom)
5414 element = EL_DC_STEELWALL_1_BOTTOM;
5417 case 0x1698: // steel wall (right)
5418 element = EL_DC_STEELWALL_1_RIGHT;
5421 case 0x1699: // steel wall (top)
5422 element = EL_DC_STEELWALL_1_TOP;
5425 case 0x169a: // steel wall (left/bottom)
5426 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5429 case 0x169b: // steel wall (right/bottom)
5430 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5433 case 0x169c: // steel wall (right/top)
5434 element = EL_DC_STEELWALL_1_TOPRIGHT;
5437 case 0x169d: // steel wall (left/top)
5438 element = EL_DC_STEELWALL_1_TOPLEFT;
5441 case 0x169e: // steel wall (right/bottom small)
5442 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5445 case 0x169f: // steel wall (left/bottom small)
5446 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5449 case 0x16a0: // steel wall (right/top small)
5450 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5453 case 0x16a1: // steel wall (left/top small)
5454 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5457 case 0x16a2: // steel wall (left/right)
5458 element = EL_DC_STEELWALL_1_VERTICAL;
5461 case 0x16a3: // steel wall (top/bottom)
5462 element = EL_DC_STEELWALL_1_HORIZONTAL;
5465 case 0x16a4: // steel wall 2 (left end)
5466 element = EL_DC_STEELWALL_2_LEFT;
5469 case 0x16a5: // steel wall 2 (right end)
5470 element = EL_DC_STEELWALL_2_RIGHT;
5473 case 0x16a6: // steel wall 2 (top end)
5474 element = EL_DC_STEELWALL_2_TOP;
5477 case 0x16a7: // steel wall 2 (bottom end)
5478 element = EL_DC_STEELWALL_2_BOTTOM;
5481 case 0x16a8: // steel wall 2 (left/right)
5482 element = EL_DC_STEELWALL_2_HORIZONTAL;
5485 case 0x16a9: // steel wall 2 (up/down)
5486 element = EL_DC_STEELWALL_2_VERTICAL;
5489 case 0x16aa: // steel wall 2 (mid)
5490 element = EL_DC_STEELWALL_2_MIDDLE;
5494 element = EL_SIGN_EXCLAMATION;
5498 element = EL_SIGN_RADIOACTIVITY;
5502 element = EL_SIGN_STOP;
5506 element = EL_SIGN_WHEELCHAIR;
5510 element = EL_SIGN_PARKING;
5514 element = EL_SIGN_NO_ENTRY;
5518 element = EL_SIGN_HEART;
5522 element = EL_SIGN_GIVE_WAY;
5526 element = EL_SIGN_ENTRY_FORBIDDEN;
5530 element = EL_SIGN_EMERGENCY_EXIT;
5534 element = EL_SIGN_YIN_YANG;
5538 element = EL_WALL_EMERALD;
5542 element = EL_WALL_DIAMOND;
5546 element = EL_WALL_PEARL;
5550 element = EL_WALL_CRYSTAL;
5554 element = EL_INVISIBLE_WALL;
5558 element = EL_INVISIBLE_STEELWALL;
5562 // EL_INVISIBLE_SAND
5565 element = EL_LIGHT_SWITCH;
5569 element = EL_ENVELOPE_1;
5573 if (element >= 0x0117 && element <= 0x036e) // (?)
5574 element = EL_DIAMOND;
5575 else if (element >= 0x042d && element <= 0x0684) // (?)
5576 element = EL_EMERALD;
5577 else if (element >= 0x157c && element <= 0x158b)
5579 else if (element >= 0x1590 && element <= 0x159f)
5580 element = EL_DC_LANDMINE;
5581 else if (element >= 0x16bc && element <= 0x16cb)
5582 element = EL_INVISIBLE_SAND;
5585 Warn("unknown Diamond Caves element 0x%04x", element);
5587 element = EL_UNKNOWN;
5592 return getMappedElement(element);
5595 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5598 byte header[DC_LEVEL_HEADER_SIZE];
5600 int envelope_header_pos = 62;
5601 int envelope_content_pos = 94;
5602 int level_name_pos = 251;
5603 int level_author_pos = 292;
5604 int envelope_header_len;
5605 int envelope_content_len;
5607 int level_author_len;
5609 int num_yamyam_contents;
5612 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5614 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5616 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5618 header[i * 2 + 0] = header_word >> 8;
5619 header[i * 2 + 1] = header_word & 0xff;
5622 // read some values from level header to check level decoding integrity
5623 fieldx = header[6] | (header[7] << 8);
5624 fieldy = header[8] | (header[9] << 8);
5625 num_yamyam_contents = header[60] | (header[61] << 8);
5627 // do some simple sanity checks to ensure that level was correctly decoded
5628 if (fieldx < 1 || fieldx > 256 ||
5629 fieldy < 1 || fieldy > 256 ||
5630 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5632 level->no_valid_file = TRUE;
5634 Warn("cannot decode level from stream -- using empty level");
5639 // maximum envelope header size is 31 bytes
5640 envelope_header_len = header[envelope_header_pos];
5641 // maximum envelope content size is 110 (156?) bytes
5642 envelope_content_len = header[envelope_content_pos];
5644 // maximum level title size is 40 bytes
5645 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5646 // maximum level author size is 30 (51?) bytes
5647 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5651 for (i = 0; i < envelope_header_len; i++)
5652 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5653 level->envelope[0].text[envelope_size++] =
5654 header[envelope_header_pos + 1 + i];
5656 if (envelope_header_len > 0 && envelope_content_len > 0)
5658 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5659 level->envelope[0].text[envelope_size++] = '\n';
5660 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5661 level->envelope[0].text[envelope_size++] = '\n';
5664 for (i = 0; i < envelope_content_len; i++)
5665 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5666 level->envelope[0].text[envelope_size++] =
5667 header[envelope_content_pos + 1 + i];
5669 level->envelope[0].text[envelope_size] = '\0';
5671 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5672 level->envelope[0].ysize = 10;
5673 level->envelope[0].autowrap = TRUE;
5674 level->envelope[0].centered = TRUE;
5676 for (i = 0; i < level_name_len; i++)
5677 level->name[i] = header[level_name_pos + 1 + i];
5678 level->name[level_name_len] = '\0';
5680 for (i = 0; i < level_author_len; i++)
5681 level->author[i] = header[level_author_pos + 1 + i];
5682 level->author[level_author_len] = '\0';
5684 num_yamyam_contents = header[60] | (header[61] << 8);
5685 level->num_yamyam_contents =
5686 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5688 for (i = 0; i < num_yamyam_contents; i++)
5690 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5692 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5693 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5695 if (i < MAX_ELEMENT_CONTENTS)
5696 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5700 fieldx = header[6] | (header[7] << 8);
5701 fieldy = header[8] | (header[9] << 8);
5702 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5703 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5705 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5707 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5708 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5710 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5711 level->field[x][y] = getMappedElement_DC(element_dc);
5714 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5715 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5716 level->field[x][y] = EL_PLAYER_1;
5718 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5719 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5720 level->field[x][y] = EL_PLAYER_2;
5722 level->gems_needed = header[18] | (header[19] << 8);
5724 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5725 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5726 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5727 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5728 level->score[SC_NUT] = header[28] | (header[29] << 8);
5729 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5730 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5731 level->score[SC_BUG] = header[34] | (header[35] << 8);
5732 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5733 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5734 level->score[SC_KEY] = header[40] | (header[41] << 8);
5735 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5737 level->time = header[44] | (header[45] << 8);
5739 level->amoeba_speed = header[46] | (header[47] << 8);
5740 level->time_light = header[48] | (header[49] << 8);
5741 level->time_timegate = header[50] | (header[51] << 8);
5742 level->time_wheel = header[52] | (header[53] << 8);
5743 level->time_magic_wall = header[54] | (header[55] << 8);
5744 level->extra_time = header[56] | (header[57] << 8);
5745 level->shield_normal_time = header[58] | (header[59] << 8);
5747 // shield and extra time elements do not have a score
5748 level->score[SC_SHIELD] = 0;
5749 level->extra_time_score = 0;
5751 // set time for normal and deadly shields to the same value
5752 level->shield_deadly_time = level->shield_normal_time;
5754 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5755 // can slip down from flat walls, like normal walls and steel walls
5756 level->em_slippery_gems = TRUE;
5758 // time score is counted for each 10 seconds left in Diamond Caves levels
5759 level->time_score_base = 10;
5762 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5763 struct LevelFileInfo *level_file_info,
5764 boolean level_info_only)
5766 char *filename = level_file_info->filename;
5768 int num_magic_bytes = 8;
5769 char magic_bytes[num_magic_bytes + 1];
5770 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5772 if (!(file = openFile(filename, MODE_READ)))
5774 level->no_valid_file = TRUE;
5776 if (!level_info_only)
5777 Warn("cannot read level '%s' -- using empty level", filename);
5782 // fseek(file, 0x0000, SEEK_SET);
5784 if (level_file_info->packed)
5786 // read "magic bytes" from start of file
5787 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5788 magic_bytes[0] = '\0';
5790 // check "magic bytes" for correct file format
5791 if (!strPrefix(magic_bytes, "DC2"))
5793 level->no_valid_file = TRUE;
5795 Warn("unknown DC level file '%s' -- using empty level", filename);
5800 if (strPrefix(magic_bytes, "DC2Win95") ||
5801 strPrefix(magic_bytes, "DC2Win98"))
5803 int position_first_level = 0x00fa;
5804 int extra_bytes = 4;
5807 // advance file stream to first level inside the level package
5808 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5810 // each block of level data is followed by block of non-level data
5811 num_levels_to_skip *= 2;
5813 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5814 while (num_levels_to_skip >= 0)
5816 // advance file stream to next level inside the level package
5817 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5819 level->no_valid_file = TRUE;
5821 Warn("cannot fseek in file '%s' -- using empty level", filename);
5826 // skip apparently unused extra bytes following each level
5827 ReadUnusedBytesFromFile(file, extra_bytes);
5829 // read size of next level in level package
5830 skip_bytes = getFile32BitLE(file);
5832 num_levels_to_skip--;
5837 level->no_valid_file = TRUE;
5839 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5845 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5851 // ----------------------------------------------------------------------------
5852 // functions for loading SB level
5853 // ----------------------------------------------------------------------------
5855 int getMappedElement_SB(int element_ascii, boolean use_ces)
5863 sb_element_mapping[] =
5865 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5866 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5867 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5868 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5869 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5870 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5871 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5872 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5879 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5880 if (element_ascii == sb_element_mapping[i].ascii)
5881 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5883 return EL_UNDEFINED;
5886 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5887 struct LevelFileInfo *level_file_info,
5888 boolean level_info_only)
5890 char *filename = level_file_info->filename;
5891 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5892 char last_comment[MAX_LINE_LEN];
5893 char level_name[MAX_LINE_LEN];
5896 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5897 boolean read_continued_line = FALSE;
5898 boolean reading_playfield = FALSE;
5899 boolean got_valid_playfield_line = FALSE;
5900 boolean invalid_playfield_char = FALSE;
5901 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5902 int file_level_nr = 0;
5904 int x = 0, y = 0; // initialized to make compilers happy
5906 last_comment[0] = '\0';
5907 level_name[0] = '\0';
5909 if (!(file = openFile(filename, MODE_READ)))
5911 level->no_valid_file = TRUE;
5913 if (!level_info_only)
5914 Warn("cannot read level '%s' -- using empty level", filename);
5919 while (!checkEndOfFile(file))
5921 // level successfully read, but next level may follow here
5922 if (!got_valid_playfield_line && reading_playfield)
5924 // read playfield from single level file -- skip remaining file
5925 if (!level_file_info->packed)
5928 if (file_level_nr >= num_levels_to_skip)
5933 last_comment[0] = '\0';
5934 level_name[0] = '\0';
5936 reading_playfield = FALSE;
5939 got_valid_playfield_line = FALSE;
5941 // read next line of input file
5942 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5945 // check if line was completely read and is terminated by line break
5946 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5949 // cut trailing line break (this can be newline and/or carriage return)
5950 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5951 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5954 // copy raw input line for later use (mainly debugging output)
5955 strcpy(line_raw, line);
5957 if (read_continued_line)
5959 // append new line to existing line, if there is enough space
5960 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5961 strcat(previous_line, line_ptr);
5963 strcpy(line, previous_line); // copy storage buffer to line
5965 read_continued_line = FALSE;
5968 // if the last character is '\', continue at next line
5969 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5971 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5972 strcpy(previous_line, line); // copy line to storage buffer
5974 read_continued_line = TRUE;
5980 if (line[0] == '\0')
5983 // extract comment text from comment line
5986 for (line_ptr = line; *line_ptr; line_ptr++)
5987 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5990 strcpy(last_comment, line_ptr);
5995 // extract level title text from line containing level title
5996 if (line[0] == '\'')
5998 strcpy(level_name, &line[1]);
6000 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6001 level_name[strlen(level_name) - 1] = '\0';
6006 // skip lines containing only spaces (or empty lines)
6007 for (line_ptr = line; *line_ptr; line_ptr++)
6008 if (*line_ptr != ' ')
6010 if (*line_ptr == '\0')
6013 // at this point, we have found a line containing part of a playfield
6015 got_valid_playfield_line = TRUE;
6017 if (!reading_playfield)
6019 reading_playfield = TRUE;
6020 invalid_playfield_char = FALSE;
6022 for (x = 0; x < MAX_LEV_FIELDX; x++)
6023 for (y = 0; y < MAX_LEV_FIELDY; y++)
6024 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6029 // start with topmost tile row
6033 // skip playfield line if larger row than allowed
6034 if (y >= MAX_LEV_FIELDY)
6037 // start with leftmost tile column
6040 // read playfield elements from line
6041 for (line_ptr = line; *line_ptr; line_ptr++)
6043 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6045 // stop parsing playfield line if larger column than allowed
6046 if (x >= MAX_LEV_FIELDX)
6049 if (mapped_sb_element == EL_UNDEFINED)
6051 invalid_playfield_char = TRUE;
6056 level->field[x][y] = mapped_sb_element;
6058 // continue with next tile column
6061 level->fieldx = MAX(x, level->fieldx);
6064 if (invalid_playfield_char)
6066 // if first playfield line, treat invalid lines as comment lines
6068 reading_playfield = FALSE;
6073 // continue with next tile row
6081 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6082 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6084 if (!reading_playfield)
6086 level->no_valid_file = TRUE;
6088 Warn("cannot read level '%s' -- using empty level", filename);
6093 if (*level_name != '\0')
6095 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6096 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6098 else if (*last_comment != '\0')
6100 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6101 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6105 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6108 // set all empty fields beyond the border walls to invisible steel wall
6109 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6111 if ((x == 0 || x == level->fieldx - 1 ||
6112 y == 0 || y == level->fieldy - 1) &&
6113 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6114 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6115 level->field, level->fieldx, level->fieldy);
6118 // set special level settings for Sokoban levels
6121 level->use_step_counter = TRUE;
6123 if (load_xsb_to_ces)
6125 // special global settings can now be set in level template
6127 level->use_custom_template = TRUE;
6132 // -------------------------------------------------------------------------
6133 // functions for handling native levels
6134 // -------------------------------------------------------------------------
6136 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6137 struct LevelFileInfo *level_file_info,
6138 boolean level_info_only)
6140 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6141 level->no_valid_file = TRUE;
6144 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6145 struct LevelFileInfo *level_file_info,
6146 boolean level_info_only)
6150 // determine position of requested level inside level package
6151 if (level_file_info->packed)
6152 pos = level_file_info->nr - leveldir_current->first_level;
6154 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6155 level->no_valid_file = TRUE;
6158 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6159 struct LevelFileInfo *level_file_info,
6160 boolean level_info_only)
6162 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6163 level->no_valid_file = TRUE;
6166 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6168 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6169 CopyNativeLevel_RND_to_EM(level);
6170 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6171 CopyNativeLevel_RND_to_SP(level);
6172 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6173 CopyNativeLevel_RND_to_MM(level);
6176 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6178 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6179 CopyNativeLevel_EM_to_RND(level);
6180 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6181 CopyNativeLevel_SP_to_RND(level);
6182 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6183 CopyNativeLevel_MM_to_RND(level);
6186 void SaveNativeLevel(struct LevelInfo *level)
6188 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6190 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6191 char *filename = getLevelFilenameFromBasename(basename);
6193 CopyNativeLevel_RND_to_SP(level);
6194 CopyNativeTape_RND_to_SP(level);
6196 SaveNativeLevel_SP(filename);
6201 // ----------------------------------------------------------------------------
6202 // functions for loading generic level
6203 // ----------------------------------------------------------------------------
6205 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6206 struct LevelFileInfo *level_file_info,
6207 boolean level_info_only)
6209 // always start with reliable default values
6210 setLevelInfoToDefaults(level, level_info_only, TRUE);
6212 switch (level_file_info->type)
6214 case LEVEL_FILE_TYPE_RND:
6215 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6218 case LEVEL_FILE_TYPE_EM:
6219 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6220 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6223 case LEVEL_FILE_TYPE_SP:
6224 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6225 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6228 case LEVEL_FILE_TYPE_MM:
6229 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6230 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6233 case LEVEL_FILE_TYPE_DC:
6234 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6237 case LEVEL_FILE_TYPE_SB:
6238 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6242 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6246 // if level file is invalid, restore level structure to default values
6247 if (level->no_valid_file)
6248 setLevelInfoToDefaults(level, level_info_only, FALSE);
6250 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6251 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6253 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6254 CopyNativeLevel_Native_to_RND(level);
6257 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6259 static struct LevelFileInfo level_file_info;
6261 // always start with reliable default values
6262 setFileInfoToDefaults(&level_file_info);
6264 level_file_info.nr = 0; // unknown level number
6265 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6267 setString(&level_file_info.filename, filename);
6269 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6272 static void LoadLevel_InitVersion(struct LevelInfo *level)
6276 if (leveldir_current == NULL) // only when dumping level
6279 // all engine modifications also valid for levels which use latest engine
6280 if (level->game_version < VERSION_IDENT(3,2,0,5))
6282 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6283 level->time_score_base = 10;
6286 if (leveldir_current->latest_engine)
6288 // ---------- use latest game engine --------------------------------------
6290 /* For all levels which are forced to use the latest game engine version
6291 (normally all but user contributed, private and undefined levels), set
6292 the game engine version to the actual version; this allows for actual
6293 corrections in the game engine to take effect for existing, converted
6294 levels (from "classic" or other existing games) to make the emulation
6295 of the corresponding game more accurate, while (hopefully) not breaking
6296 existing levels created from other players. */
6298 level->game_version = GAME_VERSION_ACTUAL;
6300 /* Set special EM style gems behaviour: EM style gems slip down from
6301 normal, steel and growing wall. As this is a more fundamental change,
6302 it seems better to set the default behaviour to "off" (as it is more
6303 natural) and make it configurable in the level editor (as a property
6304 of gem style elements). Already existing converted levels (neither
6305 private nor contributed levels) are changed to the new behaviour. */
6307 if (level->file_version < FILE_VERSION_2_0)
6308 level->em_slippery_gems = TRUE;
6313 // ---------- use game engine the level was created with --------------------
6315 /* For all levels which are not forced to use the latest game engine
6316 version (normally user contributed, private and undefined levels),
6317 use the version of the game engine the levels were created for.
6319 Since 2.0.1, the game engine version is now directly stored
6320 in the level file (chunk "VERS"), so there is no need anymore
6321 to set the game version from the file version (except for old,
6322 pre-2.0 levels, where the game version is still taken from the
6323 file format version used to store the level -- see above). */
6325 // player was faster than enemies in 1.0.0 and before
6326 if (level->file_version == FILE_VERSION_1_0)
6327 for (i = 0; i < MAX_PLAYERS; i++)
6328 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6330 // default behaviour for EM style gems was "slippery" only in 2.0.1
6331 if (level->game_version == VERSION_IDENT(2,0,1,0))
6332 level->em_slippery_gems = TRUE;
6334 // springs could be pushed over pits before (pre-release version) 2.2.0
6335 if (level->game_version < VERSION_IDENT(2,2,0,0))
6336 level->use_spring_bug = TRUE;
6338 if (level->game_version < VERSION_IDENT(3,2,0,5))
6340 // time orb caused limited time in endless time levels before 3.2.0-5
6341 level->use_time_orb_bug = TRUE;
6343 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6344 level->block_snap_field = FALSE;
6346 // extra time score was same value as time left score before 3.2.0-5
6347 level->extra_time_score = level->score[SC_TIME_BONUS];
6350 if (level->game_version < VERSION_IDENT(3,2,0,7))
6352 // default behaviour for snapping was "not continuous" before 3.2.0-7
6353 level->continuous_snapping = FALSE;
6356 // only few elements were able to actively move into acid before 3.1.0
6357 // trigger settings did not exist before 3.1.0; set to default "any"
6358 if (level->game_version < VERSION_IDENT(3,1,0,0))
6360 // correct "can move into acid" settings (all zero in old levels)
6362 level->can_move_into_acid_bits = 0; // nothing can move into acid
6363 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6365 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6366 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6367 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6368 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6370 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6371 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6373 // correct trigger settings (stored as zero == "none" in old levels)
6375 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6377 int element = EL_CUSTOM_START + i;
6378 struct ElementInfo *ei = &element_info[element];
6380 for (j = 0; j < ei->num_change_pages; j++)
6382 struct ElementChangeInfo *change = &ei->change_page[j];
6384 change->trigger_player = CH_PLAYER_ANY;
6385 change->trigger_page = CH_PAGE_ANY;
6390 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6392 int element = EL_CUSTOM_256;
6393 struct ElementInfo *ei = &element_info[element];
6394 struct ElementChangeInfo *change = &ei->change_page[0];
6396 /* This is needed to fix a problem that was caused by a bugfix in function
6397 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6398 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6399 not replace walkable elements, but instead just placed the player on it,
6400 without placing the Sokoban field under the player). Unfortunately, this
6401 breaks "Snake Bite" style levels when the snake is halfway through a door
6402 that just closes (the snake head is still alive and can be moved in this
6403 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6404 player (without Sokoban element) which then gets killed as designed). */
6406 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6407 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6408 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6409 change->target_element = EL_PLAYER_1;
6412 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6413 if (level->game_version < VERSION_IDENT(3,2,5,0))
6415 /* This is needed to fix a problem that was caused by a bugfix in function
6416 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6417 corrects the behaviour when a custom element changes to another custom
6418 element with a higher element number that has change actions defined.
6419 Normally, only one change per frame is allowed for custom elements.
6420 Therefore, it is checked if a custom element already changed in the
6421 current frame; if it did, subsequent changes are suppressed.
6422 Unfortunately, this is only checked for element changes, but not for
6423 change actions, which are still executed. As the function above loops
6424 through all custom elements from lower to higher, an element change
6425 resulting in a lower CE number won't be checked again, while a target
6426 element with a higher number will also be checked, and potential change
6427 actions will get executed for this CE, too (which is wrong), while
6428 further changes are ignored (which is correct). As this bugfix breaks
6429 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6430 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6431 behaviour for existing levels and tapes that make use of this bug */
6433 level->use_action_after_change_bug = TRUE;
6436 // not centering level after relocating player was default only in 3.2.3
6437 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6438 level->shifted_relocation = TRUE;
6440 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6441 if (level->game_version < VERSION_IDENT(3,2,6,0))
6442 level->em_explodes_by_fire = TRUE;
6444 // levels were solved by the first player entering an exit up to 4.1.0.0
6445 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6446 level->solved_by_one_player = TRUE;
6448 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6449 if (level->game_version < VERSION_IDENT(4,1,1,1))
6450 level->use_life_bugs = TRUE;
6452 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6453 if (level->game_version < VERSION_IDENT(4,1,1,1))
6454 level->sb_objects_needed = FALSE;
6456 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6457 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6458 level->finish_dig_collect = FALSE;
6461 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6465 // map elements that have changed in newer versions
6466 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6467 level->game_version);
6468 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6469 for (x = 0; x < 3; x++)
6470 for (y = 0; y < 3; y++)
6471 level->yamyam_content[i].e[x][y] =
6472 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6473 level->game_version);
6477 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6481 // map custom element change events that have changed in newer versions
6482 // (these following values were accidentally changed in version 3.0.1)
6483 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6484 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6486 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6488 int element = EL_CUSTOM_START + i;
6490 // order of checking and copying events to be mapped is important
6491 // (do not change the start and end value -- they are constant)
6492 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6494 if (HAS_CHANGE_EVENT(element, j - 2))
6496 SET_CHANGE_EVENT(element, j - 2, FALSE);
6497 SET_CHANGE_EVENT(element, j, TRUE);
6501 // order of checking and copying events to be mapped is important
6502 // (do not change the start and end value -- they are constant)
6503 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6505 if (HAS_CHANGE_EVENT(element, j - 1))
6507 SET_CHANGE_EVENT(element, j - 1, FALSE);
6508 SET_CHANGE_EVENT(element, j, TRUE);
6514 // initialize "can_change" field for old levels with only one change page
6515 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6517 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6519 int element = EL_CUSTOM_START + i;
6521 if (CAN_CHANGE(element))
6522 element_info[element].change->can_change = TRUE;
6526 // correct custom element values (for old levels without these options)
6527 if (level->game_version < VERSION_IDENT(3,1,1,0))
6529 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6531 int element = EL_CUSTOM_START + i;
6532 struct ElementInfo *ei = &element_info[element];
6534 if (ei->access_direction == MV_NO_DIRECTION)
6535 ei->access_direction = MV_ALL_DIRECTIONS;
6539 // correct custom element values (fix invalid values for all versions)
6542 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6544 int element = EL_CUSTOM_START + i;
6545 struct ElementInfo *ei = &element_info[element];
6547 for (j = 0; j < ei->num_change_pages; j++)
6549 struct ElementChangeInfo *change = &ei->change_page[j];
6551 if (change->trigger_player == CH_PLAYER_NONE)
6552 change->trigger_player = CH_PLAYER_ANY;
6554 if (change->trigger_side == CH_SIDE_NONE)
6555 change->trigger_side = CH_SIDE_ANY;
6560 // initialize "can_explode" field for old levels which did not store this
6561 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6562 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6564 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6566 int element = EL_CUSTOM_START + i;
6568 if (EXPLODES_1X1_OLD(element))
6569 element_info[element].explosion_type = EXPLODES_1X1;
6571 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6572 EXPLODES_SMASHED(element) ||
6573 EXPLODES_IMPACT(element)));
6577 // correct previously hard-coded move delay values for maze runner style
6578 if (level->game_version < VERSION_IDENT(3,1,1,0))
6580 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6582 int element = EL_CUSTOM_START + i;
6584 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6586 // previously hard-coded and therefore ignored
6587 element_info[element].move_delay_fixed = 9;
6588 element_info[element].move_delay_random = 0;
6593 // set some other uninitialized values of custom elements in older levels
6594 if (level->game_version < VERSION_IDENT(3,1,0,0))
6596 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6598 int element = EL_CUSTOM_START + i;
6600 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6602 element_info[element].explosion_delay = 17;
6603 element_info[element].ignition_delay = 8;
6608 static void LoadLevel_InitElements(struct LevelInfo *level)
6610 LoadLevel_InitStandardElements(level);
6612 if (level->file_has_custom_elements)
6613 LoadLevel_InitCustomElements(level);
6615 // initialize element properties for level editor etc.
6616 InitElementPropertiesEngine(level->game_version);
6617 InitElementPropertiesGfxElement();
6620 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6624 // map elements that have changed in newer versions
6625 for (y = 0; y < level->fieldy; y++)
6626 for (x = 0; x < level->fieldx; x++)
6627 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6628 level->game_version);
6630 // clear unused playfield data (nicer if level gets resized in editor)
6631 for (x = 0; x < MAX_LEV_FIELDX; x++)
6632 for (y = 0; y < MAX_LEV_FIELDY; y++)
6633 if (x >= level->fieldx || y >= level->fieldy)
6634 level->field[x][y] = EL_EMPTY;
6636 // copy elements to runtime playfield array
6637 for (x = 0; x < MAX_LEV_FIELDX; x++)
6638 for (y = 0; y < MAX_LEV_FIELDY; y++)
6639 Tile[x][y] = level->field[x][y];
6641 // initialize level size variables for faster access
6642 lev_fieldx = level->fieldx;
6643 lev_fieldy = level->fieldy;
6645 // determine border element for this level
6646 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6647 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6652 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6654 struct LevelFileInfo *level_file_info = &level->file_info;
6656 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6657 CopyNativeLevel_RND_to_Native(level);
6660 static void LoadLevelTemplate_LoadAndInit(void)
6662 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6664 LoadLevel_InitVersion(&level_template);
6665 LoadLevel_InitElements(&level_template);
6667 ActivateLevelTemplate();
6670 void LoadLevelTemplate(int nr)
6672 if (!fileExists(getGlobalLevelTemplateFilename()))
6674 Warn("no level template found for this level");
6679 setLevelFileInfo(&level_template.file_info, nr);
6681 LoadLevelTemplate_LoadAndInit();
6684 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6686 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6688 LoadLevelTemplate_LoadAndInit();
6691 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6693 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6695 if (level.use_custom_template)
6697 if (network_level != NULL)
6698 LoadNetworkLevelTemplate(network_level);
6700 LoadLevelTemplate(-1);
6703 LoadLevel_InitVersion(&level);
6704 LoadLevel_InitElements(&level);
6705 LoadLevel_InitPlayfield(&level);
6707 LoadLevel_InitNativeEngines(&level);
6710 void LoadLevel(int nr)
6712 SetLevelSetInfo(leveldir_current->identifier, nr);
6714 setLevelFileInfo(&level.file_info, nr);
6716 LoadLevel_LoadAndInit(NULL);
6719 void LoadLevelInfoOnly(int nr)
6721 setLevelFileInfo(&level.file_info, nr);
6723 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6726 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6728 SetLevelSetInfo(network_level->leveldir_identifier,
6729 network_level->file_info.nr);
6731 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6733 LoadLevel_LoadAndInit(network_level);
6736 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6740 chunk_size += putFileVersion(file, level->file_version);
6741 chunk_size += putFileVersion(file, level->game_version);
6746 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6750 chunk_size += putFile16BitBE(file, level->creation_date.year);
6751 chunk_size += putFile8Bit(file, level->creation_date.month);
6752 chunk_size += putFile8Bit(file, level->creation_date.day);
6757 #if ENABLE_HISTORIC_CHUNKS
6758 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6762 putFile8Bit(file, level->fieldx);
6763 putFile8Bit(file, level->fieldy);
6765 putFile16BitBE(file, level->time);
6766 putFile16BitBE(file, level->gems_needed);
6768 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6769 putFile8Bit(file, level->name[i]);
6771 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6772 putFile8Bit(file, level->score[i]);
6774 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6775 for (y = 0; y < 3; y++)
6776 for (x = 0; x < 3; x++)
6777 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6778 level->yamyam_content[i].e[x][y]));
6779 putFile8Bit(file, level->amoeba_speed);
6780 putFile8Bit(file, level->time_magic_wall);
6781 putFile8Bit(file, level->time_wheel);
6782 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6783 level->amoeba_content));
6784 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6785 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6786 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6787 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6789 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6791 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6792 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6793 putFile32BitBE(file, level->can_move_into_acid_bits);
6794 putFile8Bit(file, level->dont_collide_with_bits);
6796 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6797 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6799 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6800 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6801 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6803 putFile8Bit(file, level->game_engine_type);
6805 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6809 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6814 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6815 chunk_size += putFile8Bit(file, level->name[i]);
6820 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6825 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6826 chunk_size += putFile8Bit(file, level->author[i]);
6831 #if ENABLE_HISTORIC_CHUNKS
6832 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6837 for (y = 0; y < level->fieldy; y++)
6838 for (x = 0; x < level->fieldx; x++)
6839 if (level->encoding_16bit_field)
6840 chunk_size += putFile16BitBE(file, level->field[x][y]);
6842 chunk_size += putFile8Bit(file, level->field[x][y]);
6848 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6853 for (y = 0; y < level->fieldy; y++)
6854 for (x = 0; x < level->fieldx; x++)
6855 chunk_size += putFile16BitBE(file, level->field[x][y]);
6860 #if ENABLE_HISTORIC_CHUNKS
6861 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6865 putFile8Bit(file, EL_YAMYAM);
6866 putFile8Bit(file, level->num_yamyam_contents);
6867 putFile8Bit(file, 0);
6868 putFile8Bit(file, 0);
6870 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6871 for (y = 0; y < 3; y++)
6872 for (x = 0; x < 3; x++)
6873 if (level->encoding_16bit_field)
6874 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6876 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6880 #if ENABLE_HISTORIC_CHUNKS
6881 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6884 int num_contents, content_xsize, content_ysize;
6885 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6887 if (element == EL_YAMYAM)
6889 num_contents = level->num_yamyam_contents;
6893 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6894 for (y = 0; y < 3; y++)
6895 for (x = 0; x < 3; x++)
6896 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6898 else if (element == EL_BD_AMOEBA)
6904 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6905 for (y = 0; y < 3; y++)
6906 for (x = 0; x < 3; x++)
6907 content_array[i][x][y] = EL_EMPTY;
6908 content_array[0][0][0] = level->amoeba_content;
6912 // chunk header already written -- write empty chunk data
6913 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6915 Warn("cannot save content for element '%d'", element);
6920 putFile16BitBE(file, element);
6921 putFile8Bit(file, num_contents);
6922 putFile8Bit(file, content_xsize);
6923 putFile8Bit(file, content_ysize);
6925 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6927 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6928 for (y = 0; y < 3; y++)
6929 for (x = 0; x < 3; x++)
6930 putFile16BitBE(file, content_array[i][x][y]);
6934 #if ENABLE_HISTORIC_CHUNKS
6935 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6937 int envelope_nr = element - EL_ENVELOPE_1;
6938 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6942 chunk_size += putFile16BitBE(file, element);
6943 chunk_size += putFile16BitBE(file, envelope_len);
6944 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6945 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6947 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6948 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6950 for (i = 0; i < envelope_len; i++)
6951 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6957 #if ENABLE_HISTORIC_CHUNKS
6958 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6959 int num_changed_custom_elements)
6963 putFile16BitBE(file, num_changed_custom_elements);
6965 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6967 int element = EL_CUSTOM_START + i;
6969 struct ElementInfo *ei = &element_info[element];
6971 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6973 if (check < num_changed_custom_elements)
6975 putFile16BitBE(file, element);
6976 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6983 if (check != num_changed_custom_elements) // should not happen
6984 Warn("inconsistent number of custom element properties");
6988 #if ENABLE_HISTORIC_CHUNKS
6989 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6990 int num_changed_custom_elements)
6994 putFile16BitBE(file, num_changed_custom_elements);
6996 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6998 int element = EL_CUSTOM_START + i;
7000 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7002 if (check < num_changed_custom_elements)
7004 putFile16BitBE(file, element);
7005 putFile16BitBE(file, element_info[element].change->target_element);
7012 if (check != num_changed_custom_elements) // should not happen
7013 Warn("inconsistent number of custom target elements");
7017 #if ENABLE_HISTORIC_CHUNKS
7018 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7019 int num_changed_custom_elements)
7021 int i, j, x, y, check = 0;
7023 putFile16BitBE(file, num_changed_custom_elements);
7025 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7027 int element = EL_CUSTOM_START + i;
7028 struct ElementInfo *ei = &element_info[element];
7030 if (ei->modified_settings)
7032 if (check < num_changed_custom_elements)
7034 putFile16BitBE(file, element);
7036 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7037 putFile8Bit(file, ei->description[j]);
7039 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7041 // some free bytes for future properties and padding
7042 WriteUnusedBytesToFile(file, 7);
7044 putFile8Bit(file, ei->use_gfx_element);
7045 putFile16BitBE(file, ei->gfx_element_initial);
7047 putFile8Bit(file, ei->collect_score_initial);
7048 putFile8Bit(file, ei->collect_count_initial);
7050 putFile16BitBE(file, ei->push_delay_fixed);
7051 putFile16BitBE(file, ei->push_delay_random);
7052 putFile16BitBE(file, ei->move_delay_fixed);
7053 putFile16BitBE(file, ei->move_delay_random);
7055 putFile16BitBE(file, ei->move_pattern);
7056 putFile8Bit(file, ei->move_direction_initial);
7057 putFile8Bit(file, ei->move_stepsize);
7059 for (y = 0; y < 3; y++)
7060 for (x = 0; x < 3; x++)
7061 putFile16BitBE(file, ei->content.e[x][y]);
7063 putFile32BitBE(file, ei->change->events);
7065 putFile16BitBE(file, ei->change->target_element);
7067 putFile16BitBE(file, ei->change->delay_fixed);
7068 putFile16BitBE(file, ei->change->delay_random);
7069 putFile16BitBE(file, ei->change->delay_frames);
7071 putFile16BitBE(file, ei->change->initial_trigger_element);
7073 putFile8Bit(file, ei->change->explode);
7074 putFile8Bit(file, ei->change->use_target_content);
7075 putFile8Bit(file, ei->change->only_if_complete);
7076 putFile8Bit(file, ei->change->use_random_replace);
7078 putFile8Bit(file, ei->change->random_percentage);
7079 putFile8Bit(file, ei->change->replace_when);
7081 for (y = 0; y < 3; y++)
7082 for (x = 0; x < 3; x++)
7083 putFile16BitBE(file, ei->change->content.e[x][y]);
7085 putFile8Bit(file, ei->slippery_type);
7087 // some free bytes for future properties and padding
7088 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7095 if (check != num_changed_custom_elements) // should not happen
7096 Warn("inconsistent number of custom element properties");
7100 #if ENABLE_HISTORIC_CHUNKS
7101 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7103 struct ElementInfo *ei = &element_info[element];
7106 // ---------- custom element base property values (96 bytes) ----------------
7108 putFile16BitBE(file, element);
7110 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7111 putFile8Bit(file, ei->description[i]);
7113 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7115 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7117 putFile8Bit(file, ei->num_change_pages);
7119 putFile16BitBE(file, ei->ce_value_fixed_initial);
7120 putFile16BitBE(file, ei->ce_value_random_initial);
7121 putFile8Bit(file, ei->use_last_ce_value);
7123 putFile8Bit(file, ei->use_gfx_element);
7124 putFile16BitBE(file, ei->gfx_element_initial);
7126 putFile8Bit(file, ei->collect_score_initial);
7127 putFile8Bit(file, ei->collect_count_initial);
7129 putFile8Bit(file, ei->drop_delay_fixed);
7130 putFile8Bit(file, ei->push_delay_fixed);
7131 putFile8Bit(file, ei->drop_delay_random);
7132 putFile8Bit(file, ei->push_delay_random);
7133 putFile16BitBE(file, ei->move_delay_fixed);
7134 putFile16BitBE(file, ei->move_delay_random);
7136 // bits 0 - 15 of "move_pattern" ...
7137 putFile16BitBE(file, ei->move_pattern & 0xffff);
7138 putFile8Bit(file, ei->move_direction_initial);
7139 putFile8Bit(file, ei->move_stepsize);
7141 putFile8Bit(file, ei->slippery_type);
7143 for (y = 0; y < 3; y++)
7144 for (x = 0; x < 3; x++)
7145 putFile16BitBE(file, ei->content.e[x][y]);
7147 putFile16BitBE(file, ei->move_enter_element);
7148 putFile16BitBE(file, ei->move_leave_element);
7149 putFile8Bit(file, ei->move_leave_type);
7151 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7152 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7154 putFile8Bit(file, ei->access_direction);
7156 putFile8Bit(file, ei->explosion_delay);
7157 putFile8Bit(file, ei->ignition_delay);
7158 putFile8Bit(file, ei->explosion_type);
7160 // some free bytes for future custom property values and padding
7161 WriteUnusedBytesToFile(file, 1);
7163 // ---------- change page property values (48 bytes) ------------------------
7165 for (i = 0; i < ei->num_change_pages; i++)
7167 struct ElementChangeInfo *change = &ei->change_page[i];
7168 unsigned int event_bits;
7170 // bits 0 - 31 of "has_event[]" ...
7172 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7173 if (change->has_event[j])
7174 event_bits |= (1 << j);
7175 putFile32BitBE(file, event_bits);
7177 putFile16BitBE(file, change->target_element);
7179 putFile16BitBE(file, change->delay_fixed);
7180 putFile16BitBE(file, change->delay_random);
7181 putFile16BitBE(file, change->delay_frames);
7183 putFile16BitBE(file, change->initial_trigger_element);
7185 putFile8Bit(file, change->explode);
7186 putFile8Bit(file, change->use_target_content);
7187 putFile8Bit(file, change->only_if_complete);
7188 putFile8Bit(file, change->use_random_replace);
7190 putFile8Bit(file, change->random_percentage);
7191 putFile8Bit(file, change->replace_when);
7193 for (y = 0; y < 3; y++)
7194 for (x = 0; x < 3; x++)
7195 putFile16BitBE(file, change->target_content.e[x][y]);
7197 putFile8Bit(file, change->can_change);
7199 putFile8Bit(file, change->trigger_side);
7201 putFile8Bit(file, change->trigger_player);
7202 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7203 log_2(change->trigger_page)));
7205 putFile8Bit(file, change->has_action);
7206 putFile8Bit(file, change->action_type);
7207 putFile8Bit(file, change->action_mode);
7208 putFile16BitBE(file, change->action_arg);
7210 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7212 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7213 if (change->has_event[j])
7214 event_bits |= (1 << (j - 32));
7215 putFile8Bit(file, event_bits);
7220 #if ENABLE_HISTORIC_CHUNKS
7221 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7223 struct ElementInfo *ei = &element_info[element];
7224 struct ElementGroupInfo *group = ei->group;
7227 putFile16BitBE(file, element);
7229 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7230 putFile8Bit(file, ei->description[i]);
7232 putFile8Bit(file, group->num_elements);
7234 putFile8Bit(file, ei->use_gfx_element);
7235 putFile16BitBE(file, ei->gfx_element_initial);
7237 putFile8Bit(file, group->choice_mode);
7239 // some free bytes for future values and padding
7240 WriteUnusedBytesToFile(file, 3);
7242 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7243 putFile16BitBE(file, group->element[i]);
7247 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7248 boolean write_element)
7250 int save_type = entry->save_type;
7251 int data_type = entry->data_type;
7252 int conf_type = entry->conf_type;
7253 int byte_mask = conf_type & CONF_MASK_BYTES;
7254 int element = entry->element;
7255 int default_value = entry->default_value;
7257 boolean modified = FALSE;
7259 if (byte_mask != CONF_MASK_MULTI_BYTES)
7261 void *value_ptr = entry->value;
7262 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7265 // check if any settings have been modified before saving them
7266 if (value != default_value)
7269 // do not save if explicitly told or if unmodified default settings
7270 if ((save_type == SAVE_CONF_NEVER) ||
7271 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7275 num_bytes += putFile16BitBE(file, element);
7277 num_bytes += putFile8Bit(file, conf_type);
7278 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7279 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7280 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7283 else if (data_type == TYPE_STRING)
7285 char *default_string = entry->default_string;
7286 char *string = (char *)(entry->value);
7287 int string_length = strlen(string);
7290 // check if any settings have been modified before saving them
7291 if (!strEqual(string, default_string))
7294 // do not save if explicitly told or if unmodified default settings
7295 if ((save_type == SAVE_CONF_NEVER) ||
7296 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7300 num_bytes += putFile16BitBE(file, element);
7302 num_bytes += putFile8Bit(file, conf_type);
7303 num_bytes += putFile16BitBE(file, string_length);
7305 for (i = 0; i < string_length; i++)
7306 num_bytes += putFile8Bit(file, string[i]);
7308 else if (data_type == TYPE_ELEMENT_LIST)
7310 int *element_array = (int *)(entry->value);
7311 int num_elements = *(int *)(entry->num_entities);
7314 // check if any settings have been modified before saving them
7315 for (i = 0; i < num_elements; i++)
7316 if (element_array[i] != default_value)
7319 // do not save if explicitly told or if unmodified default settings
7320 if ((save_type == SAVE_CONF_NEVER) ||
7321 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7325 num_bytes += putFile16BitBE(file, element);
7327 num_bytes += putFile8Bit(file, conf_type);
7328 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7330 for (i = 0; i < num_elements; i++)
7331 num_bytes += putFile16BitBE(file, element_array[i]);
7333 else if (data_type == TYPE_CONTENT_LIST)
7335 struct Content *content = (struct Content *)(entry->value);
7336 int num_contents = *(int *)(entry->num_entities);
7339 // check if any settings have been modified before saving them
7340 for (i = 0; i < num_contents; i++)
7341 for (y = 0; y < 3; y++)
7342 for (x = 0; x < 3; x++)
7343 if (content[i].e[x][y] != default_value)
7346 // do not save if explicitly told or if unmodified default settings
7347 if ((save_type == SAVE_CONF_NEVER) ||
7348 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7352 num_bytes += putFile16BitBE(file, element);
7354 num_bytes += putFile8Bit(file, conf_type);
7355 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7357 for (i = 0; i < num_contents; i++)
7358 for (y = 0; y < 3; y++)
7359 for (x = 0; x < 3; x++)
7360 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7366 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7371 li = *level; // copy level data into temporary buffer
7373 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7374 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7379 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7384 li = *level; // copy level data into temporary buffer
7386 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7387 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7392 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7394 int envelope_nr = element - EL_ENVELOPE_1;
7398 chunk_size += putFile16BitBE(file, element);
7400 // copy envelope data into temporary buffer
7401 xx_envelope = level->envelope[envelope_nr];
7403 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7404 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7409 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7411 struct ElementInfo *ei = &element_info[element];
7415 chunk_size += putFile16BitBE(file, element);
7417 xx_ei = *ei; // copy element data into temporary buffer
7419 // set default description string for this specific element
7420 strcpy(xx_default_description, getDefaultElementDescription(ei));
7422 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7423 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7425 for (i = 0; i < ei->num_change_pages; i++)
7427 struct ElementChangeInfo *change = &ei->change_page[i];
7429 xx_current_change_page = i;
7431 xx_change = *change; // copy change data into temporary buffer
7434 setEventBitsFromEventFlags(change);
7436 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7437 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7444 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7446 struct ElementInfo *ei = &element_info[element];
7447 struct ElementGroupInfo *group = ei->group;
7451 chunk_size += putFile16BitBE(file, element);
7453 xx_ei = *ei; // copy element data into temporary buffer
7454 xx_group = *group; // copy group data into temporary buffer
7456 // set default description string for this specific element
7457 strcpy(xx_default_description, getDefaultElementDescription(ei));
7459 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7460 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7465 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7466 boolean save_as_template)
7472 if (!(file = fopen(filename, MODE_WRITE)))
7474 Warn("cannot save level file '%s'", filename);
7479 level->file_version = FILE_VERSION_ACTUAL;
7480 level->game_version = GAME_VERSION_ACTUAL;
7482 level->creation_date = getCurrentDate();
7484 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7485 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7487 chunk_size = SaveLevel_VERS(NULL, level);
7488 putFileChunkBE(file, "VERS", chunk_size);
7489 SaveLevel_VERS(file, level);
7491 chunk_size = SaveLevel_DATE(NULL, level);
7492 putFileChunkBE(file, "DATE", chunk_size);
7493 SaveLevel_DATE(file, level);
7495 chunk_size = SaveLevel_NAME(NULL, level);
7496 putFileChunkBE(file, "NAME", chunk_size);
7497 SaveLevel_NAME(file, level);
7499 chunk_size = SaveLevel_AUTH(NULL, level);
7500 putFileChunkBE(file, "AUTH", chunk_size);
7501 SaveLevel_AUTH(file, level);
7503 chunk_size = SaveLevel_INFO(NULL, level);
7504 putFileChunkBE(file, "INFO", chunk_size);
7505 SaveLevel_INFO(file, level);
7507 chunk_size = SaveLevel_BODY(NULL, level);
7508 putFileChunkBE(file, "BODY", chunk_size);
7509 SaveLevel_BODY(file, level);
7511 chunk_size = SaveLevel_ELEM(NULL, level);
7512 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7514 putFileChunkBE(file, "ELEM", chunk_size);
7515 SaveLevel_ELEM(file, level);
7518 for (i = 0; i < NUM_ENVELOPES; i++)
7520 int element = EL_ENVELOPE_1 + i;
7522 chunk_size = SaveLevel_NOTE(NULL, level, element);
7523 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7525 putFileChunkBE(file, "NOTE", chunk_size);
7526 SaveLevel_NOTE(file, level, element);
7530 // if not using template level, check for non-default custom/group elements
7531 if (!level->use_custom_template || save_as_template)
7533 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7535 int element = EL_CUSTOM_START + i;
7537 chunk_size = SaveLevel_CUSX(NULL, level, element);
7538 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7540 putFileChunkBE(file, "CUSX", chunk_size);
7541 SaveLevel_CUSX(file, level, element);
7545 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7547 int element = EL_GROUP_START + i;
7549 chunk_size = SaveLevel_GRPX(NULL, level, element);
7550 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7552 putFileChunkBE(file, "GRPX", chunk_size);
7553 SaveLevel_GRPX(file, level, element);
7560 SetFilePermissions(filename, PERMS_PRIVATE);
7563 void SaveLevel(int nr)
7565 char *filename = getDefaultLevelFilename(nr);
7567 SaveLevelFromFilename(&level, filename, FALSE);
7570 void SaveLevelTemplate(void)
7572 char *filename = getLocalLevelTemplateFilename();
7574 SaveLevelFromFilename(&level, filename, TRUE);
7577 boolean SaveLevelChecked(int nr)
7579 char *filename = getDefaultLevelFilename(nr);
7580 boolean new_level = !fileExists(filename);
7581 boolean level_saved = FALSE;
7583 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7588 Request("Level saved!", REQ_CONFIRM);
7596 void DumpLevel(struct LevelInfo *level)
7598 if (level->no_level_file || level->no_valid_file)
7600 Warn("cannot dump -- no valid level file found");
7606 Print("Level xxx (file version %08d, game version %08d)\n",
7607 level->file_version, level->game_version);
7610 Print("Level author: '%s'\n", level->author);
7611 Print("Level title: '%s'\n", level->name);
7613 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7615 Print("Level time: %d seconds\n", level->time);
7616 Print("Gems needed: %d\n", level->gems_needed);
7618 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7619 Print("Time for wheel: %d seconds\n", level->time_wheel);
7620 Print("Time for light: %d seconds\n", level->time_light);
7621 Print("Time for timegate: %d seconds\n", level->time_timegate);
7623 Print("Amoeba speed: %d\n", level->amoeba_speed);
7626 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7627 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7628 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7629 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7630 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7636 // ============================================================================
7637 // tape file functions
7638 // ============================================================================
7640 static void setTapeInfoToDefaults(void)
7644 // always start with reliable default values (empty tape)
7647 // default values (also for pre-1.2 tapes) with only the first player
7648 tape.player_participates[0] = TRUE;
7649 for (i = 1; i < MAX_PLAYERS; i++)
7650 tape.player_participates[i] = FALSE;
7652 // at least one (default: the first) player participates in every tape
7653 tape.num_participating_players = 1;
7655 tape.property_bits = TAPE_PROPERTY_NONE;
7657 tape.level_nr = level_nr;
7659 tape.changed = FALSE;
7661 tape.recording = FALSE;
7662 tape.playing = FALSE;
7663 tape.pausing = FALSE;
7665 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7666 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7668 tape.no_valid_file = FALSE;
7671 static int getTapePosSize(struct TapeInfo *tape)
7673 int tape_pos_size = 0;
7675 if (tape->use_key_actions)
7676 tape_pos_size += tape->num_participating_players;
7678 if (tape->use_mouse_actions)
7679 tape_pos_size += 3; // x and y position and mouse button mask
7681 tape_pos_size += 1; // tape action delay value
7683 return tape_pos_size;
7686 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7688 tape->use_key_actions = FALSE;
7689 tape->use_mouse_actions = FALSE;
7691 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7692 tape->use_key_actions = TRUE;
7694 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7695 tape->use_mouse_actions = TRUE;
7698 static int getTapeActionValue(struct TapeInfo *tape)
7700 return (tape->use_key_actions &&
7701 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7702 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7703 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7704 TAPE_ACTIONS_DEFAULT);
7707 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7709 tape->file_version = getFileVersion(file);
7710 tape->game_version = getFileVersion(file);
7715 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7719 tape->random_seed = getFile32BitBE(file);
7720 tape->date = getFile32BitBE(file);
7721 tape->length = getFile32BitBE(file);
7723 // read header fields that are new since version 1.2
7724 if (tape->file_version >= FILE_VERSION_1_2)
7726 byte store_participating_players = getFile8Bit(file);
7729 // since version 1.2, tapes store which players participate in the tape
7730 tape->num_participating_players = 0;
7731 for (i = 0; i < MAX_PLAYERS; i++)
7733 tape->player_participates[i] = FALSE;
7735 if (store_participating_players & (1 << i))
7737 tape->player_participates[i] = TRUE;
7738 tape->num_participating_players++;
7742 setTapeActionFlags(tape, getFile8Bit(file));
7744 tape->property_bits = getFile8Bit(file);
7746 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7748 engine_version = getFileVersion(file);
7749 if (engine_version > 0)
7750 tape->engine_version = engine_version;
7752 tape->engine_version = tape->game_version;
7758 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7760 tape->scr_fieldx = getFile8Bit(file);
7761 tape->scr_fieldy = getFile8Bit(file);
7766 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7768 char *level_identifier = NULL;
7769 int level_identifier_size;
7772 level_identifier_size = getFile16BitBE(file);
7774 level_identifier = checked_malloc(level_identifier_size);
7776 for (i = 0; i < level_identifier_size; i++)
7777 level_identifier[i] = getFile8Bit(file);
7779 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
7780 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
7782 checked_free(level_identifier);
7784 tape->level_nr = getFile16BitBE(file);
7786 chunk_size = 2 + level_identifier_size + 2;
7791 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7794 int tape_pos_size = getTapePosSize(tape);
7795 int chunk_size_expected = tape_pos_size * tape->length;
7797 if (chunk_size_expected != chunk_size)
7799 ReadUnusedBytesFromFile(file, chunk_size);
7800 return chunk_size_expected;
7803 for (i = 0; i < tape->length; i++)
7805 if (i >= MAX_TAPE_LEN)
7807 Warn("tape truncated -- size exceeds maximum tape size %d",
7810 // tape too large; read and ignore remaining tape data from this chunk
7811 for (;i < tape->length; i++)
7812 ReadUnusedBytesFromFile(file, tape_pos_size);
7817 if (tape->use_key_actions)
7819 for (j = 0; j < MAX_PLAYERS; j++)
7821 tape->pos[i].action[j] = MV_NONE;
7823 if (tape->player_participates[j])
7824 tape->pos[i].action[j] = getFile8Bit(file);
7828 if (tape->use_mouse_actions)
7830 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7831 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7832 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7835 tape->pos[i].delay = getFile8Bit(file);
7837 if (tape->file_version == FILE_VERSION_1_0)
7839 // eliminate possible diagonal moves in old tapes
7840 // this is only for backward compatibility
7842 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7843 byte action = tape->pos[i].action[0];
7844 int k, num_moves = 0;
7846 for (k = 0; k<4; k++)
7848 if (action & joy_dir[k])
7850 tape->pos[i + num_moves].action[0] = joy_dir[k];
7852 tape->pos[i + num_moves].delay = 0;
7861 tape->length += num_moves;
7864 else if (tape->file_version < FILE_VERSION_2_0)
7866 // convert pre-2.0 tapes to new tape format
7868 if (tape->pos[i].delay > 1)
7871 tape->pos[i + 1] = tape->pos[i];
7872 tape->pos[i + 1].delay = 1;
7875 for (j = 0; j < MAX_PLAYERS; j++)
7876 tape->pos[i].action[j] = MV_NONE;
7877 tape->pos[i].delay--;
7884 if (checkEndOfFile(file))
7888 if (i != tape->length)
7889 chunk_size = tape_pos_size * i;
7894 static void LoadTape_SokobanSolution(char *filename)
7897 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7899 if (!(file = openFile(filename, MODE_READ)))
7901 tape.no_valid_file = TRUE;
7906 while (!checkEndOfFile(file))
7908 unsigned char c = getByteFromFile(file);
7910 if (checkEndOfFile(file))
7917 tape.pos[tape.length].action[0] = MV_UP;
7918 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7924 tape.pos[tape.length].action[0] = MV_DOWN;
7925 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7931 tape.pos[tape.length].action[0] = MV_LEFT;
7932 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7938 tape.pos[tape.length].action[0] = MV_RIGHT;
7939 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7947 // ignore white-space characters
7951 tape.no_valid_file = TRUE;
7953 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
7961 if (tape.no_valid_file)
7964 tape.length_frames = GetTapeLengthFrames();
7965 tape.length_seconds = GetTapeLengthSeconds();
7968 void LoadTapeFromFilename(char *filename)
7970 char cookie[MAX_LINE_LEN];
7971 char chunk_name[CHUNK_ID_LEN + 1];
7975 // always start with reliable default values
7976 setTapeInfoToDefaults();
7978 if (strSuffix(filename, ".sln"))
7980 LoadTape_SokobanSolution(filename);
7985 if (!(file = openFile(filename, MODE_READ)))
7987 tape.no_valid_file = TRUE;
7992 getFileChunkBE(file, chunk_name, NULL);
7993 if (strEqual(chunk_name, "RND1"))
7995 getFile32BitBE(file); // not used
7997 getFileChunkBE(file, chunk_name, NULL);
7998 if (!strEqual(chunk_name, "TAPE"))
8000 tape.no_valid_file = TRUE;
8002 Warn("unknown format of tape file '%s'", filename);
8009 else // check for pre-2.0 file format with cookie string
8011 strcpy(cookie, chunk_name);
8012 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8014 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8015 cookie[strlen(cookie) - 1] = '\0';
8017 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8019 tape.no_valid_file = TRUE;
8021 Warn("unknown format of tape file '%s'", filename);
8028 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8030 tape.no_valid_file = TRUE;
8032 Warn("unsupported version of tape file '%s'", filename);
8039 // pre-2.0 tape files have no game version, so use file version here
8040 tape.game_version = tape.file_version;
8043 if (tape.file_version < FILE_VERSION_1_2)
8045 // tape files from versions before 1.2.0 without chunk structure
8046 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8047 LoadTape_BODY(file, 2 * tape.length, &tape);
8055 int (*loader)(File *, int, struct TapeInfo *);
8059 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8060 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8061 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8062 { "INFO", -1, LoadTape_INFO },
8063 { "BODY", -1, LoadTape_BODY },
8067 while (getFileChunkBE(file, chunk_name, &chunk_size))
8071 while (chunk_info[i].name != NULL &&
8072 !strEqual(chunk_name, chunk_info[i].name))
8075 if (chunk_info[i].name == NULL)
8077 Warn("unknown chunk '%s' in tape file '%s'",
8078 chunk_name, filename);
8080 ReadUnusedBytesFromFile(file, chunk_size);
8082 else if (chunk_info[i].size != -1 &&
8083 chunk_info[i].size != chunk_size)
8085 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8086 chunk_size, chunk_name, filename);
8088 ReadUnusedBytesFromFile(file, chunk_size);
8092 // call function to load this tape chunk
8093 int chunk_size_expected =
8094 (chunk_info[i].loader)(file, chunk_size, &tape);
8096 // the size of some chunks cannot be checked before reading other
8097 // chunks first (like "HEAD" and "BODY") that contain some header
8098 // information, so check them here
8099 if (chunk_size_expected != chunk_size)
8101 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8102 chunk_size, chunk_name, filename);
8110 tape.length_frames = GetTapeLengthFrames();
8111 tape.length_seconds = GetTapeLengthSeconds();
8114 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8116 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8118 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8119 tape.engine_version);
8123 void LoadTape(int nr)
8125 char *filename = getTapeFilename(nr);
8127 LoadTapeFromFilename(filename);
8130 void LoadSolutionTape(int nr)
8132 char *filename = getSolutionTapeFilename(nr);
8134 LoadTapeFromFilename(filename);
8136 if (TAPE_IS_EMPTY(tape) &&
8137 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8138 level.native_sp_level->demo.is_available)
8139 CopyNativeTape_SP_to_RND(&level);
8142 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8144 // chunk required for team mode tapes with non-default screen size
8145 return (tape->num_participating_players > 1 &&
8146 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8147 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8150 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8152 putFileVersion(file, tape->file_version);
8153 putFileVersion(file, tape->game_version);
8156 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8159 byte store_participating_players = 0;
8161 // set bits for participating players for compact storage
8162 for (i = 0; i < MAX_PLAYERS; i++)
8163 if (tape->player_participates[i])
8164 store_participating_players |= (1 << i);
8166 putFile32BitBE(file, tape->random_seed);
8167 putFile32BitBE(file, tape->date);
8168 putFile32BitBE(file, tape->length);
8170 putFile8Bit(file, store_participating_players);
8172 putFile8Bit(file, getTapeActionValue(tape));
8174 putFile8Bit(file, tape->property_bits);
8176 // unused bytes not at the end here for 4-byte alignment of engine_version
8177 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8179 putFileVersion(file, tape->engine_version);
8182 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8184 putFile8Bit(file, tape->scr_fieldx);
8185 putFile8Bit(file, tape->scr_fieldy);
8188 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8190 int level_identifier_size = strlen(tape->level_identifier) + 1;
8193 putFile16BitBE(file, level_identifier_size);
8195 for (i = 0; i < level_identifier_size; i++)
8196 putFile8Bit(file, tape->level_identifier[i]);
8198 putFile16BitBE(file, tape->level_nr);
8201 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8205 for (i = 0; i < tape->length; i++)
8207 if (tape->use_key_actions)
8209 for (j = 0; j < MAX_PLAYERS; j++)
8210 if (tape->player_participates[j])
8211 putFile8Bit(file, tape->pos[i].action[j]);
8214 if (tape->use_mouse_actions)
8216 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8217 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8218 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8221 putFile8Bit(file, tape->pos[i].delay);
8225 void SaveTapeToFilename(char *filename)
8229 int info_chunk_size;
8230 int body_chunk_size;
8232 if (!(file = fopen(filename, MODE_WRITE)))
8234 Warn("cannot save level recording file '%s'", filename);
8239 tape_pos_size = getTapePosSize(&tape);
8241 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8242 body_chunk_size = tape_pos_size * tape.length;
8244 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8245 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8247 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8248 SaveTape_VERS(file, &tape);
8250 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8251 SaveTape_HEAD(file, &tape);
8253 if (checkSaveTape_SCRN(&tape))
8255 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8256 SaveTape_SCRN(file, &tape);
8259 putFileChunkBE(file, "INFO", info_chunk_size);
8260 SaveTape_INFO(file, &tape);
8262 putFileChunkBE(file, "BODY", body_chunk_size);
8263 SaveTape_BODY(file, &tape);
8267 SetFilePermissions(filename, PERMS_PRIVATE);
8270 void SaveTape(int nr)
8272 char *filename = getTapeFilename(nr);
8275 InitTapeDirectory(leveldir_current->subdir);
8277 tape.file_version = FILE_VERSION_ACTUAL;
8278 tape.game_version = GAME_VERSION_ACTUAL;
8280 tape.num_participating_players = 0;
8282 // count number of participating players
8283 for (i = 0; i < MAX_PLAYERS; i++)
8284 if (tape.player_participates[i])
8285 tape.num_participating_players++;
8287 SaveTapeToFilename(filename);
8289 tape.changed = FALSE;
8292 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8293 unsigned int req_state_added)
8295 char *filename = getTapeFilename(nr);
8296 boolean new_tape = !fileExists(filename);
8297 boolean tape_saved = FALSE;
8299 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8304 Request(msg_saved, REQ_CONFIRM | req_state_added);
8312 boolean SaveTapeChecked(int nr)
8314 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8317 boolean SaveTapeChecked_LevelSolved(int nr)
8319 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8320 "Level solved! Tape saved!", REQ_STAY_OPEN);
8323 void DumpTape(struct TapeInfo *tape)
8325 int tape_frame_counter;
8328 if (tape->no_valid_file)
8330 Warn("cannot dump -- no valid tape file found");
8336 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8337 tape->level_nr, tape->file_version, tape->game_version);
8338 Print(" (effective engine version %08d)\n",
8339 tape->engine_version);
8340 Print("Level series identifier: '%s'\n", tape->level_identifier);
8343 tape_frame_counter = 0;
8345 for (i = 0; i < tape->length; i++)
8347 if (i >= MAX_TAPE_LEN)
8352 for (j = 0; j < MAX_PLAYERS; j++)
8354 if (tape->player_participates[j])
8356 int action = tape->pos[i].action[j];
8358 Print("%d:%02x ", j, action);
8359 Print("[%c%c%c%c|%c%c] - ",
8360 (action & JOY_LEFT ? '<' : ' '),
8361 (action & JOY_RIGHT ? '>' : ' '),
8362 (action & JOY_UP ? '^' : ' '),
8363 (action & JOY_DOWN ? 'v' : ' '),
8364 (action & JOY_BUTTON_1 ? '1' : ' '),
8365 (action & JOY_BUTTON_2 ? '2' : ' '));
8369 Print("(%03d) ", tape->pos[i].delay);
8370 Print("[%05d]\n", tape_frame_counter);
8372 tape_frame_counter += tape->pos[i].delay;
8379 // ============================================================================
8380 // score file functions
8381 // ============================================================================
8383 void LoadScore(int nr)
8386 char *filename = getScoreFilename(nr);
8387 char cookie[MAX_LINE_LEN];
8388 char line[MAX_LINE_LEN];
8392 // always start with reliable default values
8393 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8395 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8396 highscore[i].Score = 0;
8399 if (!(file = fopen(filename, MODE_READ)))
8402 // check file identifier
8403 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8405 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8406 cookie[strlen(cookie) - 1] = '\0';
8408 if (!checkCookieString(cookie, SCORE_COOKIE))
8410 Warn("unknown format of score file '%s'", filename);
8417 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8419 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8420 Warn("fscanf() failed; %s", strerror(errno));
8422 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8425 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8426 line[strlen(line) - 1] = '\0';
8428 for (line_ptr = line; *line_ptr; line_ptr++)
8430 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8432 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8433 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8442 void SaveScore(int nr)
8445 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8446 char *filename = getScoreFilename(nr);
8449 // used instead of "leveldir_current->subdir" (for network games)
8450 InitScoreDirectory(levelset.identifier);
8452 if (!(file = fopen(filename, MODE_WRITE)))
8454 Warn("cannot save score for level %d", nr);
8459 fprintf(file, "%s\n\n", SCORE_COOKIE);
8461 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8462 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8466 SetFilePermissions(filename, permissions);
8470 // ============================================================================
8471 // setup file functions
8472 // ============================================================================
8474 #define TOKEN_STR_PLAYER_PREFIX "player_"
8477 static struct TokenInfo global_setup_tokens[] =
8481 &setup.player_name, "player_name"
8485 &setup.multiple_users, "multiple_users"
8489 &setup.sound, "sound"
8493 &setup.sound_loops, "repeating_sound_loops"
8497 &setup.sound_music, "background_music"
8501 &setup.sound_simple, "simple_sound_effects"
8505 &setup.toons, "toons"
8509 &setup.scroll_delay, "scroll_delay"
8513 &setup.forced_scroll_delay, "forced_scroll_delay"
8517 &setup.scroll_delay_value, "scroll_delay_value"
8521 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8525 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8529 &setup.fade_screens, "fade_screens"
8533 &setup.autorecord, "automatic_tape_recording"
8537 &setup.show_titlescreen, "show_titlescreen"
8541 &setup.quick_doors, "quick_doors"
8545 &setup.team_mode, "team_mode"
8549 &setup.handicap, "handicap"
8553 &setup.skip_levels, "skip_levels"
8557 &setup.increment_levels, "increment_levels"
8561 &setup.auto_play_next_level, "auto_play_next_level"
8565 &setup.count_score_after_game, "count_score_after_game"
8569 &setup.show_scores_after_game, "show_scores_after_game"
8573 &setup.time_limit, "time_limit"
8577 &setup.fullscreen, "fullscreen"
8581 &setup.window_scaling_percent, "window_scaling_percent"
8585 &setup.window_scaling_quality, "window_scaling_quality"
8589 &setup.screen_rendering_mode, "screen_rendering_mode"
8593 &setup.vsync_mode, "vsync_mode"
8597 &setup.ask_on_escape, "ask_on_escape"
8601 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8605 &setup.ask_on_game_over, "ask_on_game_over"
8609 &setup.quick_switch, "quick_player_switch"
8613 &setup.input_on_focus, "input_on_focus"
8617 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8621 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
8625 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
8629 &setup.game_speed_extended, "game_speed_extended"
8633 &setup.game_frame_delay, "game_frame_delay"
8637 &setup.sp_show_border_elements, "sp_show_border_elements"
8641 &setup.small_game_graphics, "small_game_graphics"
8645 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8649 &setup.graphics_set, "graphics_set"
8653 &setup.sounds_set, "sounds_set"
8657 &setup.music_set, "music_set"
8661 &setup.override_level_graphics, "override_level_graphics"
8665 &setup.override_level_sounds, "override_level_sounds"
8669 &setup.override_level_music, "override_level_music"
8673 &setup.volume_simple, "volume_simple"
8677 &setup.volume_loops, "volume_loops"
8681 &setup.volume_music, "volume_music"
8685 &setup.network_mode, "network_mode"
8689 &setup.network_player_nr, "network_player"
8693 &setup.network_server_hostname, "network_server_hostname"
8697 &setup.touch.control_type, "touch.control_type"
8701 &setup.touch.move_distance, "touch.move_distance"
8705 &setup.touch.drop_distance, "touch.drop_distance"
8709 &setup.touch.transparency, "touch.transparency"
8713 &setup.touch.draw_outlined, "touch.draw_outlined"
8717 &setup.touch.draw_pressed, "touch.draw_pressed"
8721 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
8725 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
8729 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
8733 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
8737 static struct TokenInfo auto_setup_tokens[] =
8741 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
8745 static struct TokenInfo editor_setup_tokens[] =
8749 &setup.editor.el_classic, "editor.el_classic"
8753 &setup.editor.el_custom, "editor.el_custom"
8757 &setup.editor.el_user_defined, "editor.el_user_defined"
8761 &setup.editor.el_dynamic, "editor.el_dynamic"
8765 &setup.editor.el_headlines, "editor.el_headlines"
8769 &setup.editor.show_element_token, "editor.show_element_token"
8773 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
8777 static struct TokenInfo editor_cascade_setup_tokens[] =
8781 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
8785 &setup.editor_cascade.el_em, "editor.cascade.el_em"
8789 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
8793 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
8797 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
8801 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
8805 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
8809 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
8813 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
8817 &setup.editor_cascade.el_df, "editor.cascade.el_df"
8821 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
8825 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
8829 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
8833 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
8837 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
8841 &setup.editor_cascade.el_user, "editor.cascade.el_user"
8845 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
8849 static struct TokenInfo shortcut_setup_tokens[] =
8853 &setup.shortcut.save_game, "shortcut.save_game"
8857 &setup.shortcut.load_game, "shortcut.load_game"
8861 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
8865 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
8869 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
8873 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
8877 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
8881 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
8885 &setup.shortcut.tape_eject, "shortcut.tape_eject"
8889 &setup.shortcut.tape_extra, "shortcut.tape_extra"
8893 &setup.shortcut.tape_stop, "shortcut.tape_stop"
8897 &setup.shortcut.tape_pause, "shortcut.tape_pause"
8901 &setup.shortcut.tape_record, "shortcut.tape_record"
8905 &setup.shortcut.tape_play, "shortcut.tape_play"
8909 &setup.shortcut.sound_simple, "shortcut.sound_simple"
8913 &setup.shortcut.sound_loops, "shortcut.sound_loops"
8917 &setup.shortcut.sound_music, "shortcut.sound_music"
8921 &setup.shortcut.snap_left, "shortcut.snap_left"
8925 &setup.shortcut.snap_right, "shortcut.snap_right"
8929 &setup.shortcut.snap_up, "shortcut.snap_up"
8933 &setup.shortcut.snap_down, "shortcut.snap_down"
8937 static struct SetupInputInfo setup_input;
8938 static struct TokenInfo player_setup_tokens[] =
8942 &setup_input.use_joystick, ".use_joystick"
8946 &setup_input.joy.device_name, ".joy.device_name"
8950 &setup_input.joy.xleft, ".joy.xleft"
8954 &setup_input.joy.xmiddle, ".joy.xmiddle"
8958 &setup_input.joy.xright, ".joy.xright"
8962 &setup_input.joy.yupper, ".joy.yupper"
8966 &setup_input.joy.ymiddle, ".joy.ymiddle"
8970 &setup_input.joy.ylower, ".joy.ylower"
8974 &setup_input.joy.snap, ".joy.snap_field"
8978 &setup_input.joy.drop, ".joy.place_bomb"
8982 &setup_input.key.left, ".key.move_left"
8986 &setup_input.key.right, ".key.move_right"
8990 &setup_input.key.up, ".key.move_up"
8994 &setup_input.key.down, ".key.move_down"
8998 &setup_input.key.snap, ".key.snap_field"
9002 &setup_input.key.drop, ".key.place_bomb"
9006 static struct TokenInfo system_setup_tokens[] =
9010 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
9014 &setup.system.sdl_videodriver, "system.sdl_videodriver"
9018 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
9022 &setup.system.audio_fragment_size, "system.audio_fragment_size"
9026 static struct TokenInfo internal_setup_tokens[] =
9030 &setup.internal.program_title, "program_title"
9034 &setup.internal.program_version, "program_version"
9038 &setup.internal.program_author, "program_author"
9042 &setup.internal.program_email, "program_email"
9046 &setup.internal.program_website, "program_website"
9050 &setup.internal.program_copyright, "program_copyright"
9054 &setup.internal.program_company, "program_company"
9058 &setup.internal.program_icon_file, "program_icon_file"
9062 &setup.internal.default_graphics_set, "default_graphics_set"
9066 &setup.internal.default_sounds_set, "default_sounds_set"
9070 &setup.internal.default_music_set, "default_music_set"
9074 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
9078 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
9082 &setup.internal.fallback_music_file, "fallback_music_file"
9086 &setup.internal.default_level_series, "default_level_series"
9090 &setup.internal.default_window_width, "default_window_width"
9094 &setup.internal.default_window_height, "default_window_height"
9098 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
9102 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
9106 &setup.internal.create_user_levelset, "create_user_levelset"
9110 &setup.internal.menu_game, "menu_game"
9114 &setup.internal.menu_editor, "menu_editor"
9118 &setup.internal.menu_graphics, "menu_graphics"
9122 &setup.internal.menu_sound, "menu_sound"
9126 &setup.internal.menu_artwork, "menu_artwork"
9130 &setup.internal.menu_input, "menu_input"
9134 &setup.internal.menu_touch, "menu_touch"
9138 &setup.internal.menu_shortcuts, "menu_shortcuts"
9142 &setup.internal.menu_exit, "menu_exit"
9146 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9150 static struct TokenInfo debug_setup_tokens[] =
9154 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9158 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9162 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9166 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9170 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9174 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9178 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9182 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9186 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9190 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9194 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9198 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9202 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9206 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9210 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9214 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9218 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9222 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9226 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9230 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9234 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9237 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9241 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9245 &setup.debug.xsn_mode, "debug.xsn_mode"
9249 &setup.debug.xsn_percent, "debug.xsn_percent"
9253 static struct TokenInfo options_setup_tokens[] =
9257 &setup.options.verbose, "options.verbose"
9261 static void setSetupInfoToDefaults(struct SetupInfo *si)
9265 si->player_name = getStringCopy(getDefaultUserName(user.nr));
9267 si->multiple_users = TRUE;
9270 si->sound_loops = TRUE;
9271 si->sound_music = TRUE;
9272 si->sound_simple = TRUE;
9274 si->scroll_delay = TRUE;
9275 si->forced_scroll_delay = FALSE;
9276 si->scroll_delay_value = STD_SCROLL_DELAY;
9277 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9278 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9279 si->fade_screens = TRUE;
9280 si->autorecord = TRUE;
9281 si->show_titlescreen = TRUE;
9282 si->quick_doors = FALSE;
9283 si->team_mode = FALSE;
9284 si->handicap = TRUE;
9285 si->skip_levels = TRUE;
9286 si->increment_levels = TRUE;
9287 si->auto_play_next_level = TRUE;
9288 si->count_score_after_game = TRUE;
9289 si->show_scores_after_game = TRUE;
9290 si->time_limit = TRUE;
9291 si->fullscreen = FALSE;
9292 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9293 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9294 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9295 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9296 si->ask_on_escape = TRUE;
9297 si->ask_on_escape_editor = TRUE;
9298 si->ask_on_game_over = TRUE;
9299 si->quick_switch = FALSE;
9300 si->input_on_focus = FALSE;
9301 si->prefer_aga_graphics = TRUE;
9302 si->prefer_lowpass_sounds = FALSE;
9303 si->prefer_extra_panel_items = TRUE;
9304 si->game_speed_extended = FALSE;
9305 si->game_frame_delay = GAME_FRAME_DELAY;
9306 si->sp_show_border_elements = FALSE;
9307 si->small_game_graphics = FALSE;
9308 si->show_snapshot_buttons = FALSE;
9310 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9311 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9312 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9314 si->override_level_graphics = FALSE;
9315 si->override_level_sounds = FALSE;
9316 si->override_level_music = FALSE;
9318 si->volume_simple = 100; // percent
9319 si->volume_loops = 100; // percent
9320 si->volume_music = 100; // percent
9322 si->network_mode = FALSE;
9323 si->network_player_nr = 0; // first player
9324 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9326 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9327 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9328 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9329 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9330 si->touch.draw_outlined = TRUE;
9331 si->touch.draw_pressed = TRUE;
9333 for (i = 0; i < 2; i++)
9335 char *default_grid_button[6][2] =
9341 { "111222", " vv " },
9342 { "111222", " vv " }
9344 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9345 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9346 int min_xsize = MIN(6, grid_xsize);
9347 int min_ysize = MIN(6, grid_ysize);
9348 int startx = grid_xsize - min_xsize;
9349 int starty = grid_ysize - min_ysize;
9352 // virtual buttons grid can only be set to defaults if video is initialized
9353 // (this will be repeated if virtual buttons are not loaded from setup file)
9354 if (video.initialized)
9356 si->touch.grid_xsize[i] = grid_xsize;
9357 si->touch.grid_ysize[i] = grid_ysize;
9361 si->touch.grid_xsize[i] = -1;
9362 si->touch.grid_ysize[i] = -1;
9365 for (x = 0; x < MAX_GRID_XSIZE; x++)
9366 for (y = 0; y < MAX_GRID_YSIZE; y++)
9367 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9369 for (x = 0; x < min_xsize; x++)
9370 for (y = 0; y < min_ysize; y++)
9371 si->touch.grid_button[i][x][starty + y] =
9372 default_grid_button[y][0][x];
9374 for (x = 0; x < min_xsize; x++)
9375 for (y = 0; y < min_ysize; y++)
9376 si->touch.grid_button[i][startx + x][starty + y] =
9377 default_grid_button[y][1][x];
9380 si->touch.grid_initialized = video.initialized;
9382 si->editor.el_boulderdash = TRUE;
9383 si->editor.el_emerald_mine = TRUE;
9384 si->editor.el_emerald_mine_club = TRUE;
9385 si->editor.el_more = TRUE;
9386 si->editor.el_sokoban = TRUE;
9387 si->editor.el_supaplex = TRUE;
9388 si->editor.el_diamond_caves = TRUE;
9389 si->editor.el_dx_boulderdash = TRUE;
9391 si->editor.el_mirror_magic = TRUE;
9392 si->editor.el_deflektor = TRUE;
9394 si->editor.el_chars = TRUE;
9395 si->editor.el_steel_chars = TRUE;
9397 si->editor.el_classic = TRUE;
9398 si->editor.el_custom = TRUE;
9400 si->editor.el_user_defined = FALSE;
9401 si->editor.el_dynamic = TRUE;
9403 si->editor.el_headlines = TRUE;
9405 si->editor.show_element_token = FALSE;
9407 si->editor.show_read_only_warning = TRUE;
9409 si->editor.use_template_for_new_levels = TRUE;
9411 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9412 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9413 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9415 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9416 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9417 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9418 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9419 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9421 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9422 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9423 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9424 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9425 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9426 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9428 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9429 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9430 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9432 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9433 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9434 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9435 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9437 for (i = 0; i < MAX_PLAYERS; i++)
9439 si->input[i].use_joystick = FALSE;
9440 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9441 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9442 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9443 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9444 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9445 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9446 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9447 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9448 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9449 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9450 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9451 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9452 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9453 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9454 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9457 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9458 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9459 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9460 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9462 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9463 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9464 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9465 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9466 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9467 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9468 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9470 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9472 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9473 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9474 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9476 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9477 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9478 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9480 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9481 si->internal.choose_from_top_leveldir = FALSE;
9482 si->internal.show_scaling_in_title = TRUE;
9483 si->internal.create_user_levelset = TRUE;
9485 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9486 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9488 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9489 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9490 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9491 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9492 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9493 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9494 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9495 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9496 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9497 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9499 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9500 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9501 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9502 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9503 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9504 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9505 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9506 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9507 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9508 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9510 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9511 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9513 si->debug.show_frames_per_second = FALSE;
9515 si->debug.xsn_mode = AUTO;
9516 si->debug.xsn_percent = 0;
9518 si->options.verbose = FALSE;
9520 #if defined(PLATFORM_ANDROID)
9521 si->fullscreen = TRUE;
9524 setHideSetupEntry(&setup.debug.xsn_mode);
9527 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9529 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9532 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9534 si->editor_cascade.el_bd = TRUE;
9535 si->editor_cascade.el_em = TRUE;
9536 si->editor_cascade.el_emc = TRUE;
9537 si->editor_cascade.el_rnd = TRUE;
9538 si->editor_cascade.el_sb = TRUE;
9539 si->editor_cascade.el_sp = TRUE;
9540 si->editor_cascade.el_dc = TRUE;
9541 si->editor_cascade.el_dx = TRUE;
9543 si->editor_cascade.el_mm = TRUE;
9544 si->editor_cascade.el_df = TRUE;
9546 si->editor_cascade.el_chars = FALSE;
9547 si->editor_cascade.el_steel_chars = FALSE;
9548 si->editor_cascade.el_ce = FALSE;
9549 si->editor_cascade.el_ge = FALSE;
9550 si->editor_cascade.el_ref = FALSE;
9551 si->editor_cascade.el_user = FALSE;
9552 si->editor_cascade.el_dynamic = FALSE;
9555 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9557 static char *getHideSetupToken(void *setup_value)
9559 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9561 if (setup_value != NULL)
9562 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9564 return hide_setup_token;
9567 void setHideSetupEntry(void *setup_value)
9569 char *hide_setup_token = getHideSetupToken(setup_value);
9571 if (hide_setup_hash == NULL)
9572 hide_setup_hash = newSetupFileHash();
9574 if (setup_value != NULL)
9575 setHashEntry(hide_setup_hash, hide_setup_token, "");
9578 void removeHideSetupEntry(void *setup_value)
9580 char *hide_setup_token = getHideSetupToken(setup_value);
9582 if (setup_value != NULL)
9583 removeHashEntry(hide_setup_hash, hide_setup_token);
9586 boolean hideSetupEntry(void *setup_value)
9588 char *hide_setup_token = getHideSetupToken(setup_value);
9590 return (setup_value != NULL &&
9591 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9594 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9595 struct TokenInfo *token_info,
9596 int token_nr, char *token_text)
9598 char *token_hide_text = getStringCat2(token_text, ".hide");
9599 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9601 // set the value of this setup option in the setup option structure
9602 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9604 // check if this setup option should be hidden in the setup menu
9605 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9606 setHideSetupEntry(token_info[token_nr].value);
9608 free(token_hide_text);
9611 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9612 struct TokenInfo *token_info,
9615 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9616 token_info[token_nr].text);
9619 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9623 if (!setup_file_hash)
9626 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9627 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9629 setup.touch.grid_initialized = TRUE;
9630 for (i = 0; i < 2; i++)
9632 int grid_xsize = setup.touch.grid_xsize[i];
9633 int grid_ysize = setup.touch.grid_ysize[i];
9636 // if virtual buttons are not loaded from setup file, repeat initializing
9637 // virtual buttons grid with default values later when video is initialized
9638 if (grid_xsize == -1 ||
9641 setup.touch.grid_initialized = FALSE;
9646 for (y = 0; y < grid_ysize; y++)
9648 char token_string[MAX_LINE_LEN];
9650 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9652 char *value_string = getHashEntry(setup_file_hash, token_string);
9654 if (value_string == NULL)
9657 for (x = 0; x < grid_xsize; x++)
9659 char c = value_string[x];
9661 setup.touch.grid_button[i][x][y] =
9662 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9667 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9668 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9670 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9671 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9673 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9677 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9679 setup_input = setup.input[pnr];
9680 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9682 char full_token[100];
9684 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9685 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9688 setup.input[pnr] = setup_input;
9691 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9692 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9694 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9695 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9697 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9698 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9700 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9701 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9703 setHideRelatedSetupEntries();
9706 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9710 if (!setup_file_hash)
9713 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9714 setSetupInfo(auto_setup_tokens, i,
9715 getHashEntry(setup_file_hash,
9716 auto_setup_tokens[i].text));
9719 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9723 if (!setup_file_hash)
9726 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9727 setSetupInfo(editor_cascade_setup_tokens, i,
9728 getHashEntry(setup_file_hash,
9729 editor_cascade_setup_tokens[i].text));
9732 void LoadUserNames(void)
9734 int last_user_nr = user.nr;
9737 if (global.user_names != NULL)
9739 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9740 checked_free(global.user_names[i]);
9742 checked_free(global.user_names);
9745 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
9747 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9751 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
9753 if (setup_file_hash)
9755 char *player_name = getHashEntry(setup_file_hash, "player_name");
9757 global.user_names[i] = getFixedUserName(player_name);
9759 freeSetupFileHash(setup_file_hash);
9762 if (global.user_names[i] == NULL)
9763 global.user_names[i] = getStringCopy(getDefaultUserName(i));
9766 user.nr = last_user_nr;
9769 void LoadSetupFromFilename(char *filename)
9771 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9773 if (setup_file_hash)
9775 decodeSetupFileHash(setup_file_hash);
9777 freeSetupFileHash(setup_file_hash);
9781 Debug("setup", "using default setup values");
9785 static void LoadSetup_SpecialPostProcessing(void)
9787 char *player_name_new;
9789 // needed to work around problems with fixed length strings
9790 player_name_new = getFixedUserName(setup.player_name);
9791 free(setup.player_name);
9792 setup.player_name = player_name_new;
9794 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9795 if (setup.scroll_delay == FALSE)
9797 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9798 setup.scroll_delay = TRUE; // now always "on"
9801 // make sure that scroll delay value stays inside valid range
9802 setup.scroll_delay_value =
9803 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9806 void LoadSetup(void)
9810 // always start with reliable default values
9811 setSetupInfoToDefaults(&setup);
9813 // try to load setup values from default setup file
9814 filename = getDefaultSetupFilename();
9816 if (fileExists(filename))
9817 LoadSetupFromFilename(filename);
9819 // try to load setup values from user setup file
9820 filename = getSetupFilename();
9822 LoadSetupFromFilename(filename);
9824 LoadSetup_SpecialPostProcessing();
9827 void LoadSetup_AutoSetup(void)
9829 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9830 SetupFileHash *setup_file_hash = NULL;
9832 // always start with reliable default values
9833 setSetupInfoToDefaults_AutoSetup(&setup);
9835 setup_file_hash = loadSetupFileHash(filename);
9837 if (setup_file_hash)
9839 decodeSetupFileHash_AutoSetup(setup_file_hash);
9841 freeSetupFileHash(setup_file_hash);
9847 void LoadSetup_EditorCascade(void)
9849 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9850 SetupFileHash *setup_file_hash = NULL;
9852 // always start with reliable default values
9853 setSetupInfoToDefaults_EditorCascade(&setup);
9855 setup_file_hash = loadSetupFileHash(filename);
9857 if (setup_file_hash)
9859 decodeSetupFileHash_EditorCascade(setup_file_hash);
9861 freeSetupFileHash(setup_file_hash);
9867 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9870 char mapping_guid[MAX_LINE_LEN];
9871 char *mapping_start, *mapping_end;
9873 // get GUID from game controller mapping line: copy complete line
9874 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9875 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9877 // get GUID from game controller mapping line: cut after GUID part
9878 mapping_start = strchr(mapping_guid, ',');
9879 if (mapping_start != NULL)
9880 *mapping_start = '\0';
9882 // cut newline from game controller mapping line
9883 mapping_end = strchr(mapping_line, '\n');
9884 if (mapping_end != NULL)
9885 *mapping_end = '\0';
9887 // add mapping entry to game controller mappings hash
9888 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9891 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9896 if (!(file = fopen(filename, MODE_READ)))
9898 Warn("cannot read game controller mappings file '%s'", filename);
9905 char line[MAX_LINE_LEN];
9907 if (!fgets(line, MAX_LINE_LEN, file))
9910 addGameControllerMappingToHash(mappings_hash, line);
9916 void SaveSetup(void)
9918 char *filename = getSetupFilename();
9922 InitUserDataDirectory();
9924 if (!(file = fopen(filename, MODE_WRITE)))
9926 Warn("cannot write setup file '%s'", filename);
9931 fprintFileHeader(file, SETUP_FILENAME);
9933 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9935 // just to make things nicer :)
9936 if (global_setup_tokens[i].value == &setup.multiple_users ||
9937 global_setup_tokens[i].value == &setup.sound ||
9938 global_setup_tokens[i].value == &setup.graphics_set ||
9939 global_setup_tokens[i].value == &setup.volume_simple ||
9940 global_setup_tokens[i].value == &setup.network_mode ||
9941 global_setup_tokens[i].value == &setup.touch.control_type ||
9942 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
9943 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9944 fprintf(file, "\n");
9946 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9949 for (i = 0; i < 2; i++)
9951 int grid_xsize = setup.touch.grid_xsize[i];
9952 int grid_ysize = setup.touch.grid_ysize[i];
9955 fprintf(file, "\n");
9957 for (y = 0; y < grid_ysize; y++)
9959 char token_string[MAX_LINE_LEN];
9960 char value_string[MAX_LINE_LEN];
9962 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9964 for (x = 0; x < grid_xsize; x++)
9966 char c = setup.touch.grid_button[i][x][y];
9968 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9971 value_string[grid_xsize] = '\0';
9973 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9977 fprintf(file, "\n");
9978 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9979 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9981 fprintf(file, "\n");
9982 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9983 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9985 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9989 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9990 fprintf(file, "\n");
9992 setup_input = setup.input[pnr];
9993 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9994 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9997 fprintf(file, "\n");
9998 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9999 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
10001 // (internal setup values not saved to user setup file)
10003 fprintf(file, "\n");
10004 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10005 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
10006 setup.debug.xsn_mode != AUTO)
10007 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
10009 fprintf(file, "\n");
10010 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10011 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
10015 SetFilePermissions(filename, PERMS_PRIVATE);
10018 void SaveSetup_AutoSetup(void)
10020 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10024 InitUserDataDirectory();
10026 if (!(file = fopen(filename, MODE_WRITE)))
10028 Warn("cannot write auto setup file '%s'", filename);
10035 fprintFileHeader(file, AUTOSETUP_FILENAME);
10037 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10038 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
10042 SetFilePermissions(filename, PERMS_PRIVATE);
10047 void SaveSetup_EditorCascade(void)
10049 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10053 InitUserDataDirectory();
10055 if (!(file = fopen(filename, MODE_WRITE)))
10057 Warn("cannot write editor cascade state file '%s'", filename);
10064 fprintFileHeader(file, EDITORCASCADE_FILENAME);
10066 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10067 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
10071 SetFilePermissions(filename, PERMS_PRIVATE);
10076 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10081 if (!(file = fopen(filename, MODE_WRITE)))
10083 Warn("cannot write game controller mappings file '%s'", filename);
10088 BEGIN_HASH_ITERATION(mappings_hash, itr)
10090 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10092 END_HASH_ITERATION(mappings_hash, itr)
10097 void SaveSetup_AddGameControllerMapping(char *mapping)
10099 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10100 SetupFileHash *mappings_hash = newSetupFileHash();
10102 InitUserDataDirectory();
10104 // load existing personal game controller mappings
10105 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10107 // add new mapping to personal game controller mappings
10108 addGameControllerMappingToHash(mappings_hash, mapping);
10110 // save updated personal game controller mappings
10111 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10113 freeSetupFileHash(mappings_hash);
10117 void LoadCustomElementDescriptions(void)
10119 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10120 SetupFileHash *setup_file_hash;
10123 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10125 if (element_info[i].custom_description != NULL)
10127 free(element_info[i].custom_description);
10128 element_info[i].custom_description = NULL;
10132 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10135 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10137 char *token = getStringCat2(element_info[i].token_name, ".name");
10138 char *value = getHashEntry(setup_file_hash, token);
10141 element_info[i].custom_description = getStringCopy(value);
10146 freeSetupFileHash(setup_file_hash);
10149 static int getElementFromToken(char *token)
10151 char *value = getHashEntry(element_token_hash, token);
10154 return atoi(value);
10156 Warn("unknown element token '%s'", token);
10158 return EL_UNDEFINED;
10161 void FreeGlobalAnimEventInfo(void)
10163 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10165 if (gaei->event_list == NULL)
10170 for (i = 0; i < gaei->num_event_lists; i++)
10172 checked_free(gaei->event_list[i]->event_value);
10173 checked_free(gaei->event_list[i]);
10176 checked_free(gaei->event_list);
10178 gaei->event_list = NULL;
10179 gaei->num_event_lists = 0;
10182 static int AddGlobalAnimEventList(void)
10184 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10185 int list_pos = gaei->num_event_lists++;
10187 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10188 sizeof(struct GlobalAnimEventListInfo *));
10190 gaei->event_list[list_pos] =
10191 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10193 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10195 gaeli->event_value = NULL;
10196 gaeli->num_event_values = 0;
10201 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10203 // do not add empty global animation events
10204 if (event_value == ANIM_EVENT_NONE)
10207 // if list position is undefined, create new list
10208 if (list_pos == ANIM_EVENT_UNDEFINED)
10209 list_pos = AddGlobalAnimEventList();
10211 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10212 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10213 int value_pos = gaeli->num_event_values++;
10215 gaeli->event_value = checked_realloc(gaeli->event_value,
10216 gaeli->num_event_values * sizeof(int *));
10218 gaeli->event_value[value_pos] = event_value;
10223 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10225 if (list_pos == ANIM_EVENT_UNDEFINED)
10226 return ANIM_EVENT_NONE;
10228 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10229 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10231 return gaeli->event_value[value_pos];
10234 int GetGlobalAnimEventValueCount(int list_pos)
10236 if (list_pos == ANIM_EVENT_UNDEFINED)
10239 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10240 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10242 return gaeli->num_event_values;
10245 // This function checks if a string <s> of the format "string1, string2, ..."
10246 // exactly contains a string <s_contained>.
10248 static boolean string_has_parameter(char *s, char *s_contained)
10252 if (s == NULL || s_contained == NULL)
10255 if (strlen(s_contained) > strlen(s))
10258 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10260 char next_char = s[strlen(s_contained)];
10262 // check if next character is delimiter or whitespace
10263 return (next_char == ',' || next_char == '\0' ||
10264 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10267 // check if string contains another parameter string after a comma
10268 substring = strchr(s, ',');
10269 if (substring == NULL) // string does not contain a comma
10272 // advance string pointer to next character after the comma
10275 // skip potential whitespaces after the comma
10276 while (*substring == ' ' || *substring == '\t')
10279 return string_has_parameter(substring, s_contained);
10282 static int get_anim_parameter_value(char *s)
10284 int event_value[] =
10292 char *pattern_1[] =
10300 char *pattern_2 = ".part_";
10301 char *matching_char = NULL;
10303 int pattern_1_len = 0;
10304 int result = ANIM_EVENT_NONE;
10307 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10309 matching_char = strstr(s_ptr, pattern_1[i]);
10310 pattern_1_len = strlen(pattern_1[i]);
10311 result = event_value[i];
10313 if (matching_char != NULL)
10317 if (matching_char == NULL)
10318 return ANIM_EVENT_NONE;
10320 s_ptr = matching_char + pattern_1_len;
10322 // check for main animation number ("anim_X" or "anim_XX")
10323 if (*s_ptr >= '0' && *s_ptr <= '9')
10325 int gic_anim_nr = (*s_ptr++ - '0');
10327 if (*s_ptr >= '0' && *s_ptr <= '9')
10328 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10330 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10331 return ANIM_EVENT_NONE;
10333 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10337 // invalid main animation number specified
10339 return ANIM_EVENT_NONE;
10342 // check for animation part number ("part_X" or "part_XX") (optional)
10343 if (strPrefix(s_ptr, pattern_2))
10345 s_ptr += strlen(pattern_2);
10347 if (*s_ptr >= '0' && *s_ptr <= '9')
10349 int gic_part_nr = (*s_ptr++ - '0');
10351 if (*s_ptr >= '0' && *s_ptr <= '9')
10352 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10354 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10355 return ANIM_EVENT_NONE;
10357 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10361 // invalid animation part number specified
10363 return ANIM_EVENT_NONE;
10367 // discard result if next character is neither delimiter nor whitespace
10368 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10369 *s_ptr == ' ' || *s_ptr == '\t'))
10370 return ANIM_EVENT_NONE;
10375 static int get_anim_parameter_values(char *s)
10377 int list_pos = ANIM_EVENT_UNDEFINED;
10378 int event_value = ANIM_EVENT_DEFAULT;
10380 if (string_has_parameter(s, "any"))
10381 event_value |= ANIM_EVENT_ANY;
10383 if (string_has_parameter(s, "click:self") ||
10384 string_has_parameter(s, "click") ||
10385 string_has_parameter(s, "self"))
10386 event_value |= ANIM_EVENT_SELF;
10388 if (string_has_parameter(s, "unclick:any"))
10389 event_value |= ANIM_EVENT_UNCLICK_ANY;
10391 // if animation event found, add it to global animation event list
10392 if (event_value != ANIM_EVENT_NONE)
10393 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10397 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10398 event_value = get_anim_parameter_value(s);
10400 // if animation event found, add it to global animation event list
10401 if (event_value != ANIM_EVENT_NONE)
10402 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10404 // continue with next part of the string, starting with next comma
10405 s = strchr(s + 1, ',');
10411 static int get_anim_action_parameter_value(char *token)
10413 // check most common default case first to massively speed things up
10414 if (strEqual(token, ARG_UNDEFINED))
10415 return ANIM_EVENT_ACTION_NONE;
10417 int result = getImageIDFromToken(token);
10421 char *gfx_token = getStringCat2("gfx.", token);
10423 result = getImageIDFromToken(gfx_token);
10425 checked_free(gfx_token);
10430 Key key = getKeyFromX11KeyName(token);
10432 if (key != KSYM_UNDEFINED)
10433 result = -(int)key;
10437 result = ANIM_EVENT_ACTION_NONE;
10442 int get_parameter_value(char *value_raw, char *suffix, int type)
10444 char *value = getStringToLower(value_raw);
10445 int result = 0; // probably a save default value
10447 if (strEqual(suffix, ".direction"))
10449 result = (strEqual(value, "left") ? MV_LEFT :
10450 strEqual(value, "right") ? MV_RIGHT :
10451 strEqual(value, "up") ? MV_UP :
10452 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10454 else if (strEqual(suffix, ".position"))
10456 result = (strEqual(value, "left") ? POS_LEFT :
10457 strEqual(value, "right") ? POS_RIGHT :
10458 strEqual(value, "top") ? POS_TOP :
10459 strEqual(value, "upper") ? POS_UPPER :
10460 strEqual(value, "middle") ? POS_MIDDLE :
10461 strEqual(value, "lower") ? POS_LOWER :
10462 strEqual(value, "bottom") ? POS_BOTTOM :
10463 strEqual(value, "any") ? POS_ANY :
10464 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10466 else if (strEqual(suffix, ".align"))
10468 result = (strEqual(value, "left") ? ALIGN_LEFT :
10469 strEqual(value, "right") ? ALIGN_RIGHT :
10470 strEqual(value, "center") ? ALIGN_CENTER :
10471 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10473 else if (strEqual(suffix, ".valign"))
10475 result = (strEqual(value, "top") ? VALIGN_TOP :
10476 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10477 strEqual(value, "middle") ? VALIGN_MIDDLE :
10478 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10480 else if (strEqual(suffix, ".anim_mode"))
10482 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10483 string_has_parameter(value, "loop") ? ANIM_LOOP :
10484 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10485 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10486 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10487 string_has_parameter(value, "random") ? ANIM_RANDOM :
10488 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10489 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10490 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10491 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10492 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10493 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10494 string_has_parameter(value, "all") ? ANIM_ALL :
10497 if (string_has_parameter(value, "once"))
10498 result |= ANIM_ONCE;
10500 if (string_has_parameter(value, "reverse"))
10501 result |= ANIM_REVERSE;
10503 if (string_has_parameter(value, "opaque_player"))
10504 result |= ANIM_OPAQUE_PLAYER;
10506 if (string_has_parameter(value, "static_panel"))
10507 result |= ANIM_STATIC_PANEL;
10509 else if (strEqual(suffix, ".init_event") ||
10510 strEqual(suffix, ".anim_event"))
10512 result = get_anim_parameter_values(value);
10514 else if (strEqual(suffix, ".init_delay_action") ||
10515 strEqual(suffix, ".anim_delay_action") ||
10516 strEqual(suffix, ".post_delay_action") ||
10517 strEqual(suffix, ".init_event_action") ||
10518 strEqual(suffix, ".anim_event_action"))
10520 result = get_anim_action_parameter_value(value_raw);
10522 else if (strEqual(suffix, ".class"))
10524 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10525 get_hash_from_key(value));
10527 else if (strEqual(suffix, ".style"))
10529 result = STYLE_DEFAULT;
10531 if (string_has_parameter(value, "accurate_borders"))
10532 result |= STYLE_ACCURATE_BORDERS;
10534 if (string_has_parameter(value, "inner_corners"))
10535 result |= STYLE_INNER_CORNERS;
10537 if (string_has_parameter(value, "reverse"))
10538 result |= STYLE_REVERSE;
10540 if (string_has_parameter(value, "leftmost_position"))
10541 result |= STYLE_LEFTMOST_POSITION;
10543 if (string_has_parameter(value, "block_clicks"))
10544 result |= STYLE_BLOCK;
10546 if (string_has_parameter(value, "passthrough_clicks"))
10547 result |= STYLE_PASSTHROUGH;
10549 if (string_has_parameter(value, "multiple_actions"))
10550 result |= STYLE_MULTIPLE_ACTIONS;
10552 else if (strEqual(suffix, ".fade_mode"))
10554 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10555 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10556 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10557 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10558 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10559 FADE_MODE_DEFAULT);
10561 else if (strEqual(suffix, ".auto_delay_unit"))
10563 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10564 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10565 AUTO_DELAY_UNIT_DEFAULT);
10567 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10569 result = gfx.get_font_from_token_function(value);
10571 else // generic parameter of type integer or boolean
10573 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10574 type == TYPE_INTEGER ? get_integer_from_string(value) :
10575 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10576 ARG_UNDEFINED_VALUE);
10584 static int get_token_parameter_value(char *token, char *value_raw)
10588 if (token == NULL || value_raw == NULL)
10589 return ARG_UNDEFINED_VALUE;
10591 suffix = strrchr(token, '.');
10592 if (suffix == NULL)
10595 if (strEqual(suffix, ".element"))
10596 return getElementFromToken(value_raw);
10598 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10599 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10602 void InitMenuDesignSettings_Static(void)
10606 // always start with reliable default values from static default config
10607 for (i = 0; image_config_vars[i].token != NULL; i++)
10609 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10612 *image_config_vars[i].value =
10613 get_token_parameter_value(image_config_vars[i].token, value);
10617 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10621 // the following initializes hierarchical values from static configuration
10623 // special case: initialize "ARG_DEFAULT" values in static default config
10624 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10625 titlescreen_initial_first_default.fade_mode =
10626 title_initial_first_default.fade_mode;
10627 titlescreen_initial_first_default.fade_delay =
10628 title_initial_first_default.fade_delay;
10629 titlescreen_initial_first_default.post_delay =
10630 title_initial_first_default.post_delay;
10631 titlescreen_initial_first_default.auto_delay =
10632 title_initial_first_default.auto_delay;
10633 titlescreen_initial_first_default.auto_delay_unit =
10634 title_initial_first_default.auto_delay_unit;
10635 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10636 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10637 titlescreen_first_default.post_delay = title_first_default.post_delay;
10638 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10639 titlescreen_first_default.auto_delay_unit =
10640 title_first_default.auto_delay_unit;
10641 titlemessage_initial_first_default.fade_mode =
10642 title_initial_first_default.fade_mode;
10643 titlemessage_initial_first_default.fade_delay =
10644 title_initial_first_default.fade_delay;
10645 titlemessage_initial_first_default.post_delay =
10646 title_initial_first_default.post_delay;
10647 titlemessage_initial_first_default.auto_delay =
10648 title_initial_first_default.auto_delay;
10649 titlemessage_initial_first_default.auto_delay_unit =
10650 title_initial_first_default.auto_delay_unit;
10651 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10652 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10653 titlemessage_first_default.post_delay = title_first_default.post_delay;
10654 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10655 titlemessage_first_default.auto_delay_unit =
10656 title_first_default.auto_delay_unit;
10658 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10659 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10660 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10661 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10662 titlescreen_initial_default.auto_delay_unit =
10663 title_initial_default.auto_delay_unit;
10664 titlescreen_default.fade_mode = title_default.fade_mode;
10665 titlescreen_default.fade_delay = title_default.fade_delay;
10666 titlescreen_default.post_delay = title_default.post_delay;
10667 titlescreen_default.auto_delay = title_default.auto_delay;
10668 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10669 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10670 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10671 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10672 titlemessage_initial_default.auto_delay_unit =
10673 title_initial_default.auto_delay_unit;
10674 titlemessage_default.fade_mode = title_default.fade_mode;
10675 titlemessage_default.fade_delay = title_default.fade_delay;
10676 titlemessage_default.post_delay = title_default.post_delay;
10677 titlemessage_default.auto_delay = title_default.auto_delay;
10678 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10680 // special case: initialize "ARG_DEFAULT" values in static default config
10681 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10682 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10684 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10685 titlescreen_first[i] = titlescreen_first_default;
10686 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10687 titlemessage_first[i] = titlemessage_first_default;
10689 titlescreen_initial[i] = titlescreen_initial_default;
10690 titlescreen[i] = titlescreen_default;
10691 titlemessage_initial[i] = titlemessage_initial_default;
10692 titlemessage[i] = titlemessage_default;
10695 // special case: initialize "ARG_DEFAULT" values in static default config
10696 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10697 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10699 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
10702 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10703 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10704 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10707 // special case: initialize "ARG_DEFAULT" values in static default config
10708 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10709 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10711 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10712 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10713 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10715 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
10718 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10722 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10726 struct XY *dst, *src;
10728 game_buttons_xy[] =
10730 { &game.button.save, &game.button.stop },
10731 { &game.button.pause2, &game.button.pause },
10732 { &game.button.load, &game.button.play },
10733 { &game.button.undo, &game.button.stop },
10734 { &game.button.redo, &game.button.play },
10740 // special case: initialize later added SETUP list size from LEVELS value
10741 if (menu.list_size[GAME_MODE_SETUP] == -1)
10742 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10744 // set default position for snapshot buttons to stop/pause/play buttons
10745 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10746 if ((*game_buttons_xy[i].dst).x == -1 &&
10747 (*game_buttons_xy[i].dst).y == -1)
10748 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10750 // --------------------------------------------------------------------------
10751 // dynamic viewports (including playfield margins, borders and alignments)
10752 // --------------------------------------------------------------------------
10754 // dynamic viewports currently only supported for landscape mode
10755 int display_width = MAX(video.display_width, video.display_height);
10756 int display_height = MIN(video.display_width, video.display_height);
10758 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10760 struct RectWithBorder *vp_window = &viewport.window[i];
10761 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10762 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
10763 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
10764 boolean dynamic_window_width = (vp_window->min_width != -1);
10765 boolean dynamic_window_height = (vp_window->min_height != -1);
10766 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
10767 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10769 // adjust window size if min/max width/height is specified
10771 if (vp_window->min_width != -1)
10773 int window_width = display_width;
10775 // when using static window height, use aspect ratio of display
10776 if (vp_window->min_height == -1)
10777 window_width = vp_window->height * display_width / display_height;
10779 vp_window->width = MAX(vp_window->min_width, window_width);
10782 if (vp_window->min_height != -1)
10784 int window_height = display_height;
10786 // when using static window width, use aspect ratio of display
10787 if (vp_window->min_width == -1)
10788 window_height = vp_window->width * display_height / display_width;
10790 vp_window->height = MAX(vp_window->min_height, window_height);
10793 if (vp_window->max_width != -1)
10794 vp_window->width = MIN(vp_window->width, vp_window->max_width);
10796 if (vp_window->max_height != -1)
10797 vp_window->height = MIN(vp_window->height, vp_window->max_height);
10799 int playfield_width = vp_window->width;
10800 int playfield_height = vp_window->height;
10802 // adjust playfield size and position according to specified margins
10804 playfield_width -= vp_playfield->margin_left;
10805 playfield_width -= vp_playfield->margin_right;
10807 playfield_height -= vp_playfield->margin_top;
10808 playfield_height -= vp_playfield->margin_bottom;
10810 // adjust playfield size if min/max width/height is specified
10812 if (vp_playfield->min_width != -1)
10813 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10815 if (vp_playfield->min_height != -1)
10816 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10818 if (vp_playfield->max_width != -1)
10819 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10821 if (vp_playfield->max_height != -1)
10822 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10824 // adjust playfield position according to specified alignment
10826 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10827 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10828 else if (vp_playfield->align == ALIGN_CENTER)
10829 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10830 else if (vp_playfield->align == ALIGN_RIGHT)
10831 vp_playfield->x += playfield_width - vp_playfield->width;
10833 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10834 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10835 else if (vp_playfield->valign == VALIGN_MIDDLE)
10836 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10837 else if (vp_playfield->valign == VALIGN_BOTTOM)
10838 vp_playfield->y += playfield_height - vp_playfield->height;
10840 vp_playfield->x += vp_playfield->margin_left;
10841 vp_playfield->y += vp_playfield->margin_top;
10843 // adjust individual playfield borders if only default border is specified
10845 if (vp_playfield->border_left == -1)
10846 vp_playfield->border_left = vp_playfield->border_size;
10847 if (vp_playfield->border_right == -1)
10848 vp_playfield->border_right = vp_playfield->border_size;
10849 if (vp_playfield->border_top == -1)
10850 vp_playfield->border_top = vp_playfield->border_size;
10851 if (vp_playfield->border_bottom == -1)
10852 vp_playfield->border_bottom = vp_playfield->border_size;
10854 // set dynamic playfield borders if borders are specified as undefined
10855 // (but only if window size was dynamic and playfield size was static)
10857 if (dynamic_window_width && !dynamic_playfield_width)
10859 if (vp_playfield->border_left == -1)
10861 vp_playfield->border_left = (vp_playfield->x -
10862 vp_playfield->margin_left);
10863 vp_playfield->x -= vp_playfield->border_left;
10864 vp_playfield->width += vp_playfield->border_left;
10867 if (vp_playfield->border_right == -1)
10869 vp_playfield->border_right = (vp_window->width -
10871 vp_playfield->width -
10872 vp_playfield->margin_right);
10873 vp_playfield->width += vp_playfield->border_right;
10877 if (dynamic_window_height && !dynamic_playfield_height)
10879 if (vp_playfield->border_top == -1)
10881 vp_playfield->border_top = (vp_playfield->y -
10882 vp_playfield->margin_top);
10883 vp_playfield->y -= vp_playfield->border_top;
10884 vp_playfield->height += vp_playfield->border_top;
10887 if (vp_playfield->border_bottom == -1)
10889 vp_playfield->border_bottom = (vp_window->height -
10891 vp_playfield->height -
10892 vp_playfield->margin_bottom);
10893 vp_playfield->height += vp_playfield->border_bottom;
10897 // adjust playfield size to be a multiple of a defined alignment tile size
10899 int align_size = vp_playfield->align_size;
10900 int playfield_xtiles = vp_playfield->width / align_size;
10901 int playfield_ytiles = vp_playfield->height / align_size;
10902 int playfield_width_corrected = playfield_xtiles * align_size;
10903 int playfield_height_corrected = playfield_ytiles * align_size;
10904 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10905 i == GFX_SPECIAL_ARG_EDITOR);
10907 if (is_playfield_mode &&
10908 dynamic_playfield_width &&
10909 vp_playfield->width != playfield_width_corrected)
10911 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10913 vp_playfield->width = playfield_width_corrected;
10915 if (vp_playfield->align == ALIGN_LEFT)
10917 vp_playfield->border_left += playfield_xdiff;
10919 else if (vp_playfield->align == ALIGN_RIGHT)
10921 vp_playfield->border_right += playfield_xdiff;
10923 else if (vp_playfield->align == ALIGN_CENTER)
10925 int border_left_diff = playfield_xdiff / 2;
10926 int border_right_diff = playfield_xdiff - border_left_diff;
10928 vp_playfield->border_left += border_left_diff;
10929 vp_playfield->border_right += border_right_diff;
10933 if (is_playfield_mode &&
10934 dynamic_playfield_height &&
10935 vp_playfield->height != playfield_height_corrected)
10937 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10939 vp_playfield->height = playfield_height_corrected;
10941 if (vp_playfield->valign == VALIGN_TOP)
10943 vp_playfield->border_top += playfield_ydiff;
10945 else if (vp_playfield->align == VALIGN_BOTTOM)
10947 vp_playfield->border_right += playfield_ydiff;
10949 else if (vp_playfield->align == VALIGN_MIDDLE)
10951 int border_top_diff = playfield_ydiff / 2;
10952 int border_bottom_diff = playfield_ydiff - border_top_diff;
10954 vp_playfield->border_top += border_top_diff;
10955 vp_playfield->border_bottom += border_bottom_diff;
10959 // adjust door positions according to specified alignment
10961 for (j = 0; j < 2; j++)
10963 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10965 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10966 vp_door->x = ALIGNED_VP_XPOS(vp_door);
10967 else if (vp_door->align == ALIGN_CENTER)
10968 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10969 else if (vp_door->align == ALIGN_RIGHT)
10970 vp_door->x += vp_window->width - vp_door->width;
10972 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10973 vp_door->y = ALIGNED_VP_YPOS(vp_door);
10974 else if (vp_door->valign == VALIGN_MIDDLE)
10975 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10976 else if (vp_door->valign == VALIGN_BOTTOM)
10977 vp_door->y += vp_window->height - vp_door->height;
10982 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10986 struct XYTileSize *dst, *src;
10989 editor_buttons_xy[] =
10992 &editor.button.element_left, &editor.palette.element_left,
10993 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10996 &editor.button.element_middle, &editor.palette.element_middle,
10997 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
11000 &editor.button.element_right, &editor.palette.element_right,
11001 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
11008 // set default position for element buttons to element graphics
11009 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
11011 if ((*editor_buttons_xy[i].dst).x == -1 &&
11012 (*editor_buttons_xy[i].dst).y == -1)
11014 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
11016 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
11018 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
11022 // adjust editor palette rows and columns if specified to be dynamic
11024 if (editor.palette.cols == -1)
11026 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
11027 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
11028 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
11030 editor.palette.cols = (vp_width - sc_width) / bt_width;
11032 if (editor.palette.x == -1)
11034 int palette_width = editor.palette.cols * bt_width + sc_width;
11036 editor.palette.x = (vp_width - palette_width) / 2;
11040 if (editor.palette.rows == -1)
11042 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
11043 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
11044 int tx_height = getFontHeight(FONT_TEXT_2);
11046 editor.palette.rows = (vp_height - tx_height) / bt_height;
11048 if (editor.palette.y == -1)
11050 int palette_height = editor.palette.rows * bt_height + tx_height;
11052 editor.palette.y = (vp_height - palette_height) / 2;
11057 static void LoadMenuDesignSettingsFromFilename(char *filename)
11059 static struct TitleFadingInfo tfi;
11060 static struct TitleMessageInfo tmi;
11061 static struct TokenInfo title_tokens[] =
11063 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
11064 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
11065 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
11066 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
11067 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
11071 static struct TokenInfo titlemessage_tokens[] =
11073 { TYPE_INTEGER, &tmi.x, ".x" },
11074 { TYPE_INTEGER, &tmi.y, ".y" },
11075 { TYPE_INTEGER, &tmi.width, ".width" },
11076 { TYPE_INTEGER, &tmi.height, ".height" },
11077 { TYPE_INTEGER, &tmi.chars, ".chars" },
11078 { TYPE_INTEGER, &tmi.lines, ".lines" },
11079 { TYPE_INTEGER, &tmi.align, ".align" },
11080 { TYPE_INTEGER, &tmi.valign, ".valign" },
11081 { TYPE_INTEGER, &tmi.font, ".font" },
11082 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
11083 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
11084 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
11085 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
11086 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
11087 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
11088 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
11089 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
11090 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
11096 struct TitleFadingInfo *info;
11101 // initialize first titles from "enter screen" definitions, if defined
11102 { &title_initial_first_default, "menu.enter_screen.TITLE" },
11103 { &title_first_default, "menu.enter_screen.TITLE" },
11105 // initialize title screens from "next screen" definitions, if defined
11106 { &title_initial_default, "menu.next_screen.TITLE" },
11107 { &title_default, "menu.next_screen.TITLE" },
11113 struct TitleMessageInfo *array;
11116 titlemessage_arrays[] =
11118 // initialize first titles from "enter screen" definitions, if defined
11119 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
11120 { titlescreen_first, "menu.enter_screen.TITLE" },
11121 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
11122 { titlemessage_first, "menu.enter_screen.TITLE" },
11124 // initialize titles from "next screen" definitions, if defined
11125 { titlescreen_initial, "menu.next_screen.TITLE" },
11126 { titlescreen, "menu.next_screen.TITLE" },
11127 { titlemessage_initial, "menu.next_screen.TITLE" },
11128 { titlemessage, "menu.next_screen.TITLE" },
11130 // overwrite titles with title definitions, if defined
11131 { titlescreen_initial_first, "[title_initial]" },
11132 { titlescreen_first, "[title]" },
11133 { titlemessage_initial_first, "[title_initial]" },
11134 { titlemessage_first, "[title]" },
11136 { titlescreen_initial, "[title_initial]" },
11137 { titlescreen, "[title]" },
11138 { titlemessage_initial, "[title_initial]" },
11139 { titlemessage, "[title]" },
11141 // overwrite titles with title screen/message definitions, if defined
11142 { titlescreen_initial_first, "[titlescreen_initial]" },
11143 { titlescreen_first, "[titlescreen]" },
11144 { titlemessage_initial_first, "[titlemessage_initial]" },
11145 { titlemessage_first, "[titlemessage]" },
11147 { titlescreen_initial, "[titlescreen_initial]" },
11148 { titlescreen, "[titlescreen]" },
11149 { titlemessage_initial, "[titlemessage_initial]" },
11150 { titlemessage, "[titlemessage]" },
11154 SetupFileHash *setup_file_hash;
11157 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11160 // the following initializes hierarchical values from dynamic configuration
11162 // special case: initialize with default values that may be overwritten
11163 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11164 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11166 struct TokenIntPtrInfo menu_config[] =
11168 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
11169 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
11170 { "menu.list_size", &menu.list_size[i] }
11173 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11175 char *token = menu_config[j].token;
11176 char *value = getHashEntry(setup_file_hash, token);
11179 *menu_config[j].value = get_integer_from_string(value);
11183 // special case: initialize with default values that may be overwritten
11184 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11185 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11187 struct TokenIntPtrInfo menu_config[] =
11189 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
11190 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
11191 { "menu.list_size.INFO", &menu.list_size_info[i] }
11194 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11196 char *token = menu_config[j].token;
11197 char *value = getHashEntry(setup_file_hash, token);
11200 *menu_config[j].value = get_integer_from_string(value);
11204 // special case: initialize with default values that may be overwritten
11205 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11206 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11208 struct TokenIntPtrInfo menu_config[] =
11210 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
11211 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
11214 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11216 char *token = menu_config[j].token;
11217 char *value = getHashEntry(setup_file_hash, token);
11220 *menu_config[j].value = get_integer_from_string(value);
11224 // special case: initialize with default values that may be overwritten
11225 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11226 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11228 struct TokenIntPtrInfo menu_config[] =
11230 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11231 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11232 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11233 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11234 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11235 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11236 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11237 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11238 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11241 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11243 char *token = menu_config[j].token;
11244 char *value = getHashEntry(setup_file_hash, token);
11247 *menu_config[j].value = get_integer_from_string(value);
11251 // special case: initialize with default values that may be overwritten
11252 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11253 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11255 struct TokenIntPtrInfo menu_config[] =
11257 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11258 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11259 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11260 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11261 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11262 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11263 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11264 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11265 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11268 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11270 char *token = menu_config[j].token;
11271 char *value = getHashEntry(setup_file_hash, token);
11274 *menu_config[j].value = get_token_parameter_value(token, value);
11278 // special case: initialize with default values that may be overwritten
11279 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11280 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11284 char *token_prefix;
11285 struct RectWithBorder *struct_ptr;
11289 { "viewport.window", &viewport.window[i] },
11290 { "viewport.playfield", &viewport.playfield[i] },
11291 { "viewport.door_1", &viewport.door_1[i] },
11292 { "viewport.door_2", &viewport.door_2[i] }
11295 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11297 struct TokenIntPtrInfo vp_config[] =
11299 { ".x", &vp_struct[j].struct_ptr->x },
11300 { ".y", &vp_struct[j].struct_ptr->y },
11301 { ".width", &vp_struct[j].struct_ptr->width },
11302 { ".height", &vp_struct[j].struct_ptr->height },
11303 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11304 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11305 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11306 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11307 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11308 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11309 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11310 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11311 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11312 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11313 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11314 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11315 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11316 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11317 { ".align", &vp_struct[j].struct_ptr->align },
11318 { ".valign", &vp_struct[j].struct_ptr->valign }
11321 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11323 char *token = getStringCat2(vp_struct[j].token_prefix,
11324 vp_config[k].token);
11325 char *value = getHashEntry(setup_file_hash, token);
11328 *vp_config[k].value = get_token_parameter_value(token, value);
11335 // special case: initialize with default values that may be overwritten
11336 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11337 for (i = 0; title_info[i].info != NULL; i++)
11339 struct TitleFadingInfo *info = title_info[i].info;
11340 char *base_token = title_info[i].text;
11342 for (j = 0; title_tokens[j].type != -1; j++)
11344 char *token = getStringCat2(base_token, title_tokens[j].text);
11345 char *value = getHashEntry(setup_file_hash, token);
11349 int parameter_value = get_token_parameter_value(token, value);
11353 *(int *)title_tokens[j].value = (int)parameter_value;
11362 // special case: initialize with default values that may be overwritten
11363 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11364 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11366 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11367 char *base_token = titlemessage_arrays[i].text;
11369 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11371 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11372 char *value = getHashEntry(setup_file_hash, token);
11376 int parameter_value = get_token_parameter_value(token, value);
11378 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11382 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11383 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11385 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11395 // special case: check if network and preview player positions are redefined,
11396 // to compare this later against the main menu level preview being redefined
11397 struct TokenIntPtrInfo menu_config_players[] =
11399 { "main.network_players.x", &menu.main.network_players.redefined },
11400 { "main.network_players.y", &menu.main.network_players.redefined },
11401 { "main.preview_players.x", &menu.main.preview_players.redefined },
11402 { "main.preview_players.y", &menu.main.preview_players.redefined },
11403 { "preview.x", &preview.redefined },
11404 { "preview.y", &preview.redefined }
11407 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11408 *menu_config_players[i].value = FALSE;
11410 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11411 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11412 *menu_config_players[i].value = TRUE;
11414 // read (and overwrite with) values that may be specified in config file
11415 for (i = 0; image_config_vars[i].token != NULL; i++)
11417 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11419 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11420 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11421 *image_config_vars[i].value =
11422 get_token_parameter_value(image_config_vars[i].token, value);
11425 freeSetupFileHash(setup_file_hash);
11428 void LoadMenuDesignSettings(void)
11430 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11432 InitMenuDesignSettings_Static();
11433 InitMenuDesignSettings_SpecialPreProcessing();
11435 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11437 // first look for special settings configured in level series config
11438 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11440 if (fileExists(filename_base))
11441 LoadMenuDesignSettingsFromFilename(filename_base);
11444 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11446 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11447 LoadMenuDesignSettingsFromFilename(filename_local);
11449 InitMenuDesignSettings_SpecialPostProcessing();
11452 void LoadMenuDesignSettings_AfterGraphics(void)
11454 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11457 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11459 char *filename = getEditorSetupFilename();
11460 SetupFileList *setup_file_list, *list;
11461 SetupFileHash *element_hash;
11462 int num_unknown_tokens = 0;
11465 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11468 element_hash = newSetupFileHash();
11470 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11471 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11473 // determined size may be larger than needed (due to unknown elements)
11475 for (list = setup_file_list; list != NULL; list = list->next)
11478 // add space for up to 3 more elements for padding that may be needed
11479 *num_elements += 3;
11481 // free memory for old list of elements, if needed
11482 checked_free(*elements);
11484 // allocate memory for new list of elements
11485 *elements = checked_malloc(*num_elements * sizeof(int));
11488 for (list = setup_file_list; list != NULL; list = list->next)
11490 char *value = getHashEntry(element_hash, list->token);
11492 if (value == NULL) // try to find obsolete token mapping
11494 char *mapped_token = get_mapped_token(list->token);
11496 if (mapped_token != NULL)
11498 value = getHashEntry(element_hash, mapped_token);
11500 free(mapped_token);
11506 (*elements)[(*num_elements)++] = atoi(value);
11510 if (num_unknown_tokens == 0)
11513 Warn("unknown token(s) found in config file:");
11514 Warn("- config file: '%s'", filename);
11516 num_unknown_tokens++;
11519 Warn("- token: '%s'", list->token);
11523 if (num_unknown_tokens > 0)
11526 while (*num_elements % 4) // pad with empty elements, if needed
11527 (*elements)[(*num_elements)++] = EL_EMPTY;
11529 freeSetupFileList(setup_file_list);
11530 freeSetupFileHash(element_hash);
11533 for (i = 0; i < *num_elements; i++)
11534 Debug("editor", "element '%s' [%d]\n",
11535 element_info[(*elements)[i]].token_name, (*elements)[i]);
11539 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11542 SetupFileHash *setup_file_hash = NULL;
11543 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11544 char *filename_music, *filename_prefix, *filename_info;
11550 token_to_value_ptr[] =
11552 { "title_header", &tmp_music_file_info.title_header },
11553 { "artist_header", &tmp_music_file_info.artist_header },
11554 { "album_header", &tmp_music_file_info.album_header },
11555 { "year_header", &tmp_music_file_info.year_header },
11557 { "title", &tmp_music_file_info.title },
11558 { "artist", &tmp_music_file_info.artist },
11559 { "album", &tmp_music_file_info.album },
11560 { "year", &tmp_music_file_info.year },
11566 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11567 getCustomMusicFilename(basename));
11569 if (filename_music == NULL)
11572 // ---------- try to replace file extension ----------
11574 filename_prefix = getStringCopy(filename_music);
11575 if (strrchr(filename_prefix, '.') != NULL)
11576 *strrchr(filename_prefix, '.') = '\0';
11577 filename_info = getStringCat2(filename_prefix, ".txt");
11579 if (fileExists(filename_info))
11580 setup_file_hash = loadSetupFileHash(filename_info);
11582 free(filename_prefix);
11583 free(filename_info);
11585 if (setup_file_hash == NULL)
11587 // ---------- try to add file extension ----------
11589 filename_prefix = getStringCopy(filename_music);
11590 filename_info = getStringCat2(filename_prefix, ".txt");
11592 if (fileExists(filename_info))
11593 setup_file_hash = loadSetupFileHash(filename_info);
11595 free(filename_prefix);
11596 free(filename_info);
11599 if (setup_file_hash == NULL)
11602 // ---------- music file info found ----------
11604 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11606 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11608 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11610 *token_to_value_ptr[i].value_ptr =
11611 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11614 tmp_music_file_info.basename = getStringCopy(basename);
11615 tmp_music_file_info.music = music;
11616 tmp_music_file_info.is_sound = is_sound;
11618 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11619 *new_music_file_info = tmp_music_file_info;
11621 return new_music_file_info;
11624 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11626 return get_music_file_info_ext(basename, music, FALSE);
11629 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11631 return get_music_file_info_ext(basename, sound, TRUE);
11634 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11635 char *basename, boolean is_sound)
11637 for (; list != NULL; list = list->next)
11638 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11644 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11646 return music_info_listed_ext(list, basename, FALSE);
11649 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11651 return music_info_listed_ext(list, basename, TRUE);
11654 void LoadMusicInfo(void)
11656 char *music_directory = getCustomMusicDirectory();
11657 int num_music = getMusicListSize();
11658 int num_music_noconf = 0;
11659 int num_sounds = getSoundListSize();
11661 DirectoryEntry *dir_entry;
11662 struct FileInfo *music, *sound;
11663 struct MusicFileInfo *next, **new;
11666 while (music_file_info != NULL)
11668 next = music_file_info->next;
11670 checked_free(music_file_info->basename);
11672 checked_free(music_file_info->title_header);
11673 checked_free(music_file_info->artist_header);
11674 checked_free(music_file_info->album_header);
11675 checked_free(music_file_info->year_header);
11677 checked_free(music_file_info->title);
11678 checked_free(music_file_info->artist);
11679 checked_free(music_file_info->album);
11680 checked_free(music_file_info->year);
11682 free(music_file_info);
11684 music_file_info = next;
11687 new = &music_file_info;
11689 for (i = 0; i < num_music; i++)
11691 music = getMusicListEntry(i);
11693 if (music->filename == NULL)
11696 if (strEqual(music->filename, UNDEFINED_FILENAME))
11699 // a configured file may be not recognized as music
11700 if (!FileIsMusic(music->filename))
11703 if (!music_info_listed(music_file_info, music->filename))
11705 *new = get_music_file_info(music->filename, i);
11708 new = &(*new)->next;
11712 if ((dir = openDirectory(music_directory)) == NULL)
11714 Warn("cannot read music directory '%s'", music_directory);
11719 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
11721 char *basename = dir_entry->basename;
11722 boolean music_already_used = FALSE;
11725 // skip all music files that are configured in music config file
11726 for (i = 0; i < num_music; i++)
11728 music = getMusicListEntry(i);
11730 if (music->filename == NULL)
11733 if (strEqual(basename, music->filename))
11735 music_already_used = TRUE;
11740 if (music_already_used)
11743 if (!FileIsMusic(dir_entry->filename))
11746 if (!music_info_listed(music_file_info, basename))
11748 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11751 new = &(*new)->next;
11754 num_music_noconf++;
11757 closeDirectory(dir);
11759 for (i = 0; i < num_sounds; i++)
11761 sound = getSoundListEntry(i);
11763 if (sound->filename == NULL)
11766 if (strEqual(sound->filename, UNDEFINED_FILENAME))
11769 // a configured file may be not recognized as sound
11770 if (!FileIsSound(sound->filename))
11773 if (!sound_info_listed(music_file_info, sound->filename))
11775 *new = get_sound_file_info(sound->filename, i);
11777 new = &(*new)->next;
11782 static void add_helpanim_entry(int element, int action, int direction,
11783 int delay, int *num_list_entries)
11785 struct HelpAnimInfo *new_list_entry;
11786 (*num_list_entries)++;
11789 checked_realloc(helpanim_info,
11790 *num_list_entries * sizeof(struct HelpAnimInfo));
11791 new_list_entry = &helpanim_info[*num_list_entries - 1];
11793 new_list_entry->element = element;
11794 new_list_entry->action = action;
11795 new_list_entry->direction = direction;
11796 new_list_entry->delay = delay;
11799 static void print_unknown_token(char *filename, char *token, int token_nr)
11804 Warn("unknown token(s) found in config file:");
11805 Warn("- config file: '%s'", filename);
11808 Warn("- token: '%s'", token);
11811 static void print_unknown_token_end(int token_nr)
11817 void LoadHelpAnimInfo(void)
11819 char *filename = getHelpAnimFilename();
11820 SetupFileList *setup_file_list = NULL, *list;
11821 SetupFileHash *element_hash, *action_hash, *direction_hash;
11822 int num_list_entries = 0;
11823 int num_unknown_tokens = 0;
11826 if (fileExists(filename))
11827 setup_file_list = loadSetupFileList(filename);
11829 if (setup_file_list == NULL)
11831 // use reliable default values from static configuration
11832 SetupFileList *insert_ptr;
11834 insert_ptr = setup_file_list =
11835 newSetupFileList(helpanim_config[0].token,
11836 helpanim_config[0].value);
11838 for (i = 1; helpanim_config[i].token; i++)
11839 insert_ptr = addListEntry(insert_ptr,
11840 helpanim_config[i].token,
11841 helpanim_config[i].value);
11844 element_hash = newSetupFileHash();
11845 action_hash = newSetupFileHash();
11846 direction_hash = newSetupFileHash();
11848 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11849 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11851 for (i = 0; i < NUM_ACTIONS; i++)
11852 setHashEntry(action_hash, element_action_info[i].suffix,
11853 i_to_a(element_action_info[i].value));
11855 // do not store direction index (bit) here, but direction value!
11856 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11857 setHashEntry(direction_hash, element_direction_info[i].suffix,
11858 i_to_a(1 << element_direction_info[i].value));
11860 for (list = setup_file_list; list != NULL; list = list->next)
11862 char *element_token, *action_token, *direction_token;
11863 char *element_value, *action_value, *direction_value;
11864 int delay = atoi(list->value);
11866 if (strEqual(list->token, "end"))
11868 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11873 /* first try to break element into element/action/direction parts;
11874 if this does not work, also accept combined "element[.act][.dir]"
11875 elements (like "dynamite.active"), which are unique elements */
11877 if (strchr(list->token, '.') == NULL) // token contains no '.'
11879 element_value = getHashEntry(element_hash, list->token);
11880 if (element_value != NULL) // element found
11881 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11882 &num_list_entries);
11885 // no further suffixes found -- this is not an element
11886 print_unknown_token(filename, list->token, num_unknown_tokens++);
11892 // token has format "<prefix>.<something>"
11894 action_token = strchr(list->token, '.'); // suffix may be action ...
11895 direction_token = action_token; // ... or direction
11897 element_token = getStringCopy(list->token);
11898 *strchr(element_token, '.') = '\0';
11900 element_value = getHashEntry(element_hash, element_token);
11902 if (element_value == NULL) // this is no element
11904 element_value = getHashEntry(element_hash, list->token);
11905 if (element_value != NULL) // combined element found
11906 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11907 &num_list_entries);
11909 print_unknown_token(filename, list->token, num_unknown_tokens++);
11911 free(element_token);
11916 action_value = getHashEntry(action_hash, action_token);
11918 if (action_value != NULL) // action found
11920 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11921 &num_list_entries);
11923 free(element_token);
11928 direction_value = getHashEntry(direction_hash, direction_token);
11930 if (direction_value != NULL) // direction found
11932 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11933 &num_list_entries);
11935 free(element_token);
11940 if (strchr(action_token + 1, '.') == NULL)
11942 // no further suffixes found -- this is not an action nor direction
11944 element_value = getHashEntry(element_hash, list->token);
11945 if (element_value != NULL) // combined element found
11946 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11947 &num_list_entries);
11949 print_unknown_token(filename, list->token, num_unknown_tokens++);
11951 free(element_token);
11956 // token has format "<prefix>.<suffix>.<something>"
11958 direction_token = strchr(action_token + 1, '.');
11960 action_token = getStringCopy(action_token);
11961 *strchr(action_token + 1, '.') = '\0';
11963 action_value = getHashEntry(action_hash, action_token);
11965 if (action_value == NULL) // this is no action
11967 element_value = getHashEntry(element_hash, list->token);
11968 if (element_value != NULL) // combined element found
11969 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11970 &num_list_entries);
11972 print_unknown_token(filename, list->token, num_unknown_tokens++);
11974 free(element_token);
11975 free(action_token);
11980 direction_value = getHashEntry(direction_hash, direction_token);
11982 if (direction_value != NULL) // direction found
11984 add_helpanim_entry(atoi(element_value), atoi(action_value),
11985 atoi(direction_value), delay, &num_list_entries);
11987 free(element_token);
11988 free(action_token);
11993 // this is no direction
11995 element_value = getHashEntry(element_hash, list->token);
11996 if (element_value != NULL) // combined element found
11997 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11998 &num_list_entries);
12000 print_unknown_token(filename, list->token, num_unknown_tokens++);
12002 free(element_token);
12003 free(action_token);
12006 print_unknown_token_end(num_unknown_tokens);
12008 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
12009 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
12011 freeSetupFileList(setup_file_list);
12012 freeSetupFileHash(element_hash);
12013 freeSetupFileHash(action_hash);
12014 freeSetupFileHash(direction_hash);
12017 for (i = 0; i < num_list_entries; i++)
12018 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
12019 EL_NAME(helpanim_info[i].element),
12020 helpanim_info[i].element,
12021 helpanim_info[i].action,
12022 helpanim_info[i].direction,
12023 helpanim_info[i].delay);
12027 void LoadHelpTextInfo(void)
12029 char *filename = getHelpTextFilename();
12032 if (helptext_info != NULL)
12034 freeSetupFileHash(helptext_info);
12035 helptext_info = NULL;
12038 if (fileExists(filename))
12039 helptext_info = loadSetupFileHash(filename);
12041 if (helptext_info == NULL)
12043 // use reliable default values from static configuration
12044 helptext_info = newSetupFileHash();
12046 for (i = 0; helptext_config[i].token; i++)
12047 setHashEntry(helptext_info,
12048 helptext_config[i].token,
12049 helptext_config[i].value);
12053 BEGIN_HASH_ITERATION(helptext_info, itr)
12055 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
12056 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
12058 END_HASH_ITERATION(hash, itr)
12063 // ----------------------------------------------------------------------------
12065 // ----------------------------------------------------------------------------
12067 #define MAX_NUM_CONVERT_LEVELS 1000
12069 void ConvertLevels(void)
12071 static LevelDirTree *convert_leveldir = NULL;
12072 static int convert_level_nr = -1;
12073 static int num_levels_handled = 0;
12074 static int num_levels_converted = 0;
12075 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
12078 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12079 global.convert_leveldir);
12081 if (convert_leveldir == NULL)
12082 Fail("no such level identifier: '%s'", global.convert_leveldir);
12084 leveldir_current = convert_leveldir;
12086 if (global.convert_level_nr != -1)
12088 convert_leveldir->first_level = global.convert_level_nr;
12089 convert_leveldir->last_level = global.convert_level_nr;
12092 convert_level_nr = convert_leveldir->first_level;
12094 PrintLine("=", 79);
12095 Print("Converting levels\n");
12096 PrintLine("-", 79);
12097 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12098 Print("Level series name: '%s'\n", convert_leveldir->name);
12099 Print("Level series author: '%s'\n", convert_leveldir->author);
12100 Print("Number of levels: %d\n", convert_leveldir->levels);
12101 PrintLine("=", 79);
12104 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12105 levels_failed[i] = FALSE;
12107 while (convert_level_nr <= convert_leveldir->last_level)
12109 char *level_filename;
12112 level_nr = convert_level_nr++;
12114 Print("Level %03d: ", level_nr);
12116 LoadLevel(level_nr);
12117 if (level.no_level_file || level.no_valid_file)
12119 Print("(no level)\n");
12123 Print("converting level ... ");
12125 level_filename = getDefaultLevelFilename(level_nr);
12126 new_level = !fileExists(level_filename);
12130 SaveLevel(level_nr);
12132 num_levels_converted++;
12134 Print("converted.\n");
12138 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12139 levels_failed[level_nr] = TRUE;
12141 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12144 num_levels_handled++;
12148 PrintLine("=", 79);
12149 Print("Number of levels handled: %d\n", num_levels_handled);
12150 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12151 (num_levels_handled ?
12152 num_levels_converted * 100 / num_levels_handled : 0));
12153 PrintLine("-", 79);
12154 Print("Summary (for automatic parsing by scripts):\n");
12155 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12156 convert_leveldir->identifier, num_levels_converted,
12157 num_levels_handled,
12158 (num_levels_handled ?
12159 num_levels_converted * 100 / num_levels_handled : 0));
12161 if (num_levels_handled != num_levels_converted)
12163 Print(", FAILED:");
12164 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12165 if (levels_failed[i])
12170 PrintLine("=", 79);
12172 CloseAllAndExit(0);
12176 // ----------------------------------------------------------------------------
12177 // create and save images for use in level sketches (raw BMP format)
12178 // ----------------------------------------------------------------------------
12180 void CreateLevelSketchImages(void)
12186 InitElementPropertiesGfxElement();
12188 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12189 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12191 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12193 int element = getMappedElement(i);
12194 char basename1[16];
12195 char basename2[16];
12199 sprintf(basename1, "%04d.bmp", i);
12200 sprintf(basename2, "%04ds.bmp", i);
12202 filename1 = getPath2(global.create_images_dir, basename1);
12203 filename2 = getPath2(global.create_images_dir, basename2);
12205 DrawSizedElement(0, 0, element, TILESIZE);
12206 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12208 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12209 Fail("cannot save level sketch image file '%s'", filename1);
12211 DrawSizedElement(0, 0, element, MINI_TILESIZE);
12212 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12214 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12215 Fail("cannot save level sketch image file '%s'", filename2);
12220 // create corresponding SQL statements (for normal and small images)
12223 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12224 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12227 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12228 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12230 // optional: create content for forum level sketch demonstration post
12232 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12235 FreeBitmap(bitmap1);
12236 FreeBitmap(bitmap2);
12239 fprintf(stderr, "\n");
12241 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12243 CloseAllAndExit(0);
12247 // ----------------------------------------------------------------------------
12248 // create and save images for custom and group elements (raw BMP format)
12249 // ----------------------------------------------------------------------------
12251 void CreateCustomElementImages(char *directory)
12253 char *src_basename = "RocksCE-template.ilbm";
12254 char *dst_basename = "RocksCE.bmp";
12255 char *src_filename = getPath2(directory, src_basename);
12256 char *dst_filename = getPath2(directory, dst_basename);
12257 Bitmap *src_bitmap;
12259 int yoffset_ce = 0;
12260 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12263 InitVideoDefaults();
12265 ReCreateBitmap(&backbuffer, video.width, video.height);
12267 src_bitmap = LoadImage(src_filename);
12269 bitmap = CreateBitmap(TILEX * 16 * 2,
12270 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12273 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12280 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12281 TILEX * x, TILEY * y + yoffset_ce);
12283 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12285 TILEX * x + TILEX * 16,
12286 TILEY * y + yoffset_ce);
12288 for (j = 2; j >= 0; j--)
12292 BlitBitmap(src_bitmap, bitmap,
12293 TILEX + c * 7, 0, 6, 10,
12294 TILEX * x + 6 + j * 7,
12295 TILEY * y + 11 + yoffset_ce);
12297 BlitBitmap(src_bitmap, bitmap,
12298 TILEX + c * 8, TILEY, 6, 10,
12299 TILEX * 16 + TILEX * x + 6 + j * 8,
12300 TILEY * y + 10 + yoffset_ce);
12306 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12313 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12314 TILEX * x, TILEY * y + yoffset_ge);
12316 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12318 TILEX * x + TILEX * 16,
12319 TILEY * y + yoffset_ge);
12321 for (j = 1; j >= 0; j--)
12325 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12326 TILEX * x + 6 + j * 10,
12327 TILEY * y + 11 + yoffset_ge);
12329 BlitBitmap(src_bitmap, bitmap,
12330 TILEX + c * 8, TILEY + 12, 6, 10,
12331 TILEX * 16 + TILEX * x + 10 + j * 8,
12332 TILEY * y + 10 + yoffset_ge);
12338 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12339 Fail("cannot save CE graphics file '%s'", dst_filename);
12341 FreeBitmap(bitmap);
12343 CloseAllAndExit(0);